🔥函数式与 React:为什么 Hooks 天然适合 FP?
——useState 是 State Monad?useReducer 是纯函数驱动?
“React 不是函数式框架,
但它把函数式编程的精髓,
编译成了 DOM。”
一、Hooks 的设计哲学:函数即组件
组件 = 纯函数(理想)
js
const Profile = ({ user }) => {
return <div>{user.name}</div>;
};- 输入:
props - 输出:UI(虚拟 DOM)
- 无副作用 → 可预测、可测试
这就是函数式的核心思想:纯函数映射。
二、useState:状态的“封装”与“转换”
传统类组件的问题
js
class Counter extends Component {
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 }); // 命令式修改
}
}- 状态分散
- 方法依赖
this - 难以复用逻辑
useState 的函数式抽象
js
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);关键洞察:
setCount不是直接修改- 它是调度一个状态转换
- 下次渲染时,
count是新值
这正是 State Monad 的行为!
三、useState ≈ State Monad?
回顾:Monad 的核心
- 封装值(
Promise,Maybe) chain/flatMap实现序列化操作
State Monad 模拟
js
// State Monad: S -> (A, S)
const State = run => ({
run,
map: f => State(s => {
const [a, s1] = run(s);
return [f(a), s1];
}),
chain: f => State(s => {
const [a, s1] = run(s);
return f(a).run(s1);
})
});useState 的类比
js
// 初始状态
const [state, setState] = useState(initial);
// setState 类似于 State Monad 的“状态转换”
setState(newState);
// 相当于 调度一个 (S -> S) 函数虽然 useState 不暴露 chain,但它的组合方式符合 Monad 思想:
js
const [a, setA] = useState(1);
const [b, setB] = useState(2);
// 多个状态独立,通过 props 向下传递 —— 类似 Monad 的组合四、useReducer:纯函数驱动的状态管理
useReducer API
js
const [state, dispatch] = useReducer(reducer, initialState);reducer 必须是纯函数!
js
const reducer = (state, action) => {
switch (action.type) {
case 'INC':
return { ...state, count: state.count + 1 };
case 'RESET':
return { ...state, count: 0 };
default:
return state;
}
};- 输入:
state + action - 输出:
newState - 无副作用、可重放、可时间旅行
这正是函数式中“状态机”的典范!
五、Hooks 就是高阶函数的组合
自定义 Hook:逻辑复用的函数式方式
js
const useLocalStorage = (key, initialValue) => {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};特性:
- 纯函数结构(输入 props,返回状态)
- 闭包封装私有状态
- 副作用隔离(
useEffect) - 可组合(像普通函数一样调用)
js
const [theme, setTheme] = useLocalStorage('theme', 'dark');这不就是高阶函数 + 闭包 + 副作用管理吗?
六、React Fiber 架构:函数式的底层支持
React 16+ 的 Fiber Reconciler
- 将 UI 分解为“工作单元”(Fiber nodes)
- 可中断、可暂停、可优先级调度
与函数式思想一致:
- 不可变性:每次更新生成新 Fiber 树
- 惰性求值:按需计算 diff
- 组合优先:组件树即函数调用栈
七、函数式优势在 React 中的体现
| 优势 | 在 React Hooks 中的表现 |
|---|---|
| 可测试性 | 组件是纯函数,易单元测试 |
| 可复用性 | 自定义 Hook 跨组件共享逻辑 |
| 可预测性 | useReducer + 纯 reducer 易调试 |
| 可组合性 | useX → useY → useZ 链式构建复杂逻辑 |
八、但也有限制:不是完全的 FP
1. 副作用必须用 useEffect 显式声明
js
useEffect(() => {
// 副作用(订阅、DOM 操作)
}, []);这是对纯函数的妥协。
2. 性能优化仍需 useMemo / useCallback
js
const expensive = useMemo(() => calc(data), [data]);避免不必要的重新计算 —— 应对“中间数组”问题。
结语:Hooks 是函数式思想的胜利
它证明了:
- UI 可以是纯函数的输出
- 状态可以是转换的序列
- 逻辑可以像函数一样组合与复用
useState 虽不完全是 State Monad,
但它让状态变更变得可声明、可组合。
useReducer 虽运行在命令式环境,
但其 reducer 是纯粹的数学函数。
React 并没有强迫你写 FP,
但它为你提供了通往函数式的大门。
当你写出:
js
const [state, dispatch] = useReducer(reducer, init);你已经在用纯函数驱动应用状态了。
这才是真正的“Learn Once, Write Anywhere”。