
关于
在 React 中使用 fp-ts 的实用模式——Hook、状态、表单、数据获取。兼容 React 18/19、Next.js 14/15。
name: fp-react description: 在 React 中使用 fp-ts 的实用模式——hooks、状态、表单、数据获取。兼容 React 18/19、Next.js 14/15。 risk: unknown source: community version: 2.0.0 author: fp-ts-skills tags: [fp-ts, react, typescript, hooks, state-management, forms, data-fetching, remote-data, react-19, next-js]
React 中的函数式编程
React 应用的实用模式。没有术语,只有能用的代码。
快速参考
| 模式 | 使用场景 |
|---------|----------|
| Option | 值可能不存在(用户尚未加载) |
| Either | 操作可能失败(表单验证) |
| TaskEither | 异步操作可能失败(API 调用) |
| RemoteData | 需要显示加载/错误/成功状态 |
| pipe | 链式多个转换 |
1. 使用 Option 管理状态(可能存在,可能不存在)
使用 Option 代替 null | undefined 以获得更清晰的意图。
基本模式
import { useState } from 'react'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
interface User {
id: string
name: string
email: string
}
function UserProfile() {
// Option says "this might not exist yet"
const [user, setUser] = useState<O.Option<User>>(O.none)
const handleLogin = (userData: User) => {
setUser(O.some(userData))
}
const handleLogout = () => {
setUser(O.none)
}
return pipe(
user,
O.match(
// When there's no user
() => <button onClick={() => handleLogin({ id: '1', name: 'Alice', email: 'alice@example.com' })}>
Log In
</button>,
// When there's a user
(u) => (
<div>
<p>Welcome, {u.name}!</p>
<button onClick={handleLogout}>Log Out</button>
</div>
)
)
)
}
链式可选值
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
interface Profile {
user: O.Option<{
name: string
settings: O.Option<{
theme: string
}>
}>
}
function getTheme(profile: Profile): string {
return pipe(
profile.user,
O.flatMap(u => u.settings),
O.map(s => s.theme),
O.getOrElse(() => 'light')
)
}
2. 使用 Either 进行表单验证
使用 Either 进行可能失败的操作——左侧是错误,右侧是成功。
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
interface SignupForm {
name: string
email: string
password: string
}
function validateForm(form: SignupForm): E.Either<string[], ValidatedForm> {
return pipe(
validateAll({
name: pipe(validateName(form.name), E.mapLeft(e => [e])),
email: pipe(validateEmail(form.email), E.mapLeft(e => [e])),
password: pipe(validatePassword(form.password), E.mapLeft(e => [e])),
})
)
}
// Usage in component
function SignupForm() {
const [form, setForm] = useState({ name: '', email: '', password: '' })
const [errors, setErrors] = useState<string[]>([])
const handleSubmit = () => {
pipe(
validateForm(form),
E.match(
(errs) => setErrors(errs),
(valid) => {
setErrors([])
submitToServer(valid)
}
)
)
}
return (
<form onSubmit={e => { e.preventDefault(); handleSubmit() }}>
<input
value={form.name}
onChange={e => setForm(f => ({ ...f, name: e.target.value }))}
placeholder="Name"
/>
<input
value={form.email}
onChange={e => setForm(f => ({ ...f, email: e.target.value }))}
placeholder="Email"
/>
<input
type="password"
value={form.password}
onChange={e => setForm(f => ({ ...f, password: e.target.value }))}
placeholder="Password"
/>
{errors.length > 0 && (
<ul style={{ color: 'red' }}>
{errors.map((err, i) => <li key={i}>{err}</li>)}
</ul>
)}
<button type="submit">Sign Up</button>
</form>
)
}
兼容工具
Claude CodeCursor
标签
前端开发