React Hooks → Flutter 等价写法
适合 React 开发者快速上手 Flutter,直接从你熟悉的 Hooks 映射
总览对照表
| React Hook | Flutter 等价 | 说明 |
|---|---|---|
useState |
State 字段 + setState |
响应式状态 |
useEffect(() => {}, []) |
initState |
挂载时执行一次 |
useEffect(() => {}, [dep]) |
didUpdateWidget |
依赖变化时执行 |
useEffect(() => { return () => {} }) |
dispose |
卸载时清理 |
useRef |
State 字段(不调用 setState) | 不触发重建的引用 |
useMemo |
const Widget / 手动缓存字段 |
缓存计算结果 |
useCallback |
State 类方法(天然稳定) | 缓存函数引用 |
useContext |
InheritedWidget / Provider.of |
跨层级读取数据 |
useReducer |
Bloc / 手写 reducer | 复杂状态管理 |
useLayoutEffect |
addPostFrameCallback |
渲染后同步执行 |
forwardRef |
GlobalKey |
父组件访问子组件 |
React.memo |
const Widget |
避免不必要重建 |
lazy + Suspense |
FutureBuilder |
异步加载 |
createPortal |
Overlay |
渲染到根层 |
一、useState → State 字段 + setState
jsx
// React
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [user, setUser] = useState(null);
setCount(c => c + 1); // 函数式更新
dart
// Flutter
class _MyState extends State<MyWidget> {
int count = 0;
String name = '';
User? user; // null 用 ? 表示可空
void increment() {
setState(() {
count += 1; // 直接修改,无需函数式更新
});
}
}
差异 :Flutter 直接修改字段值,不需要
prev => prev + 1这种函数式更新。setState的作用只是通知框架重建,不负责计算新值。
二、useEffect → initState / didUpdateWidget / dispose
2.1 挂载时执行一次(依赖数组为空 [])
jsx
// React
useEffect(() => {
fetchData();
startTimer();
}, []);
dart
// Flutter
@override
void initState() {
super.initState();
fetchData();
startTimer();
}
2.2 依赖变化时执行(依赖数组有值 [dep])
jsx
// React
useEffect(() => {
fetchUser(userId);
}, [userId]);
dart
// Flutter
@override
void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 对比新旧 widget 属性,手动判断是否变化
if (oldWidget.userId != widget.userId) {
fetchUser(widget.userId);
}
}
2.3 卸载时清理(return 清理函数)
jsx
// React
useEffect(() => {
final sub = stream.listen(handler);
return () => sub.cancel(); // 清理
}, []);
dart
// Flutter
StreamSubscription? _sub;
@override
void initState() {
super.initState();
_sub = stream.listen(handler);
}
@override
void dispose() {
_sub?.cancel(); // 清理
super.dispose();
}
2.4 每次 render 后执行(无依赖数组)
jsx
// React
useEffect(() => {
console.log('每次渲染后执行');
});
dart
// Flutter ------ 不推荐,但可以在 build 末尾用 addPostFrameCallback
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
// 每次 build 完成后执行
});
return ...;
}
三、useRef → 普通字段(不触发重建)
jsx
// React
const timerRef = useRef(null);
const inputRef = useRef(null);
timerRef.current = setTimeout(...);
inputRef.current.focus();
dart
// Flutter
// 普通字段就是 useRef,修改它不触发 setState 就不会重建
Timer? _timer;
final FocusNode _focusNode = FocusNode();
// 访问 Widget 实例(等价于 inputRef.current)
final GlobalKey _key = GlobalKey();
规律 :React 需要
useRef是因为函数组件每次 render 都会重新声明变量。 Flutter 的 State 类只创建一次,普通字段天然就是"ref"。
四、useMemo → const Widget / 缓存字段
jsx
// React
const expensiveValue = useMemo(() => {
return heavyCalculation(data);
}, [data]);
const element = useMemo(() => <ExpensiveComponent />, []);
dart
// Flutter
// 缓存计算结果:手动在 initState 或 didUpdateWidget 里计算并存字段
List<Item> _sorted = [];
@override
void initState() {
super.initState();
_sorted = [...widget.items]..sort(); // 只算一次
}
// 缓存 Widget:用 const,编译期复用,父 build 重建不影响它
const ExpensiveWidget() // ✅ 等价于 useMemo(() => <Component />, [])
五、useCallback → State 类方法(天然稳定)
jsx
// React ------ 每次 render 函数引用会变,需要 useCallback 稳定引用
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
dart
// Flutter ------ 方法定义在 State 类里,引用天然稳定,不需要任何处理
void _handleClick() {
setState(() => count++);
}
// 使用
TextButton(onPressed: _handleClick, child: Text('点击'))
差异:Flutter 完全不存在这个问题,State 类方法不会在每次 build 时重新创建。
六、useContext → Provider.of / context.read
jsx
// React
const theme = useContext(ThemeContext);
const { user, setUser } = useContext(UserContext);
dart
// Flutter(以 Provider 包为例,对应 React Context)
// 读取并订阅(数据变化时重建,等价于 useContext)
final theme = context.watch<ThemeModel>();
// 只读取一次,不订阅(等价于 useRef 读 context,不触发重建)
final user = context.read<UserModel>();
// 调用方法
context.read<UserModel>().logout();
七、useReducer → Bloc / 手写 reducer
jsx
// React
const reducer = (state, action) => {
switch (action.type) {
case 'increment': return { ...state, count: state.count + 1 };
case 'reset': return { count: 0 };
}
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'increment' });
dart
// Flutter ------ 手写 reducer(轻量场景)
class CountState {
final int count;
const CountState(this.count);
}
CountState reducer(CountState state, String action) {
switch (action) {
case 'increment': return CountState(state.count + 1);
case 'reset': return const CountState(0);
default: return state;
}
}
class _MyState extends State<MyWidget> {
CountState _state = const CountState(0);
void dispatch(String action) {
setState(() => _state = reducer(_state, action));
}
}
八、useLayoutEffect → addPostFrameCallback
jsx
// React ------ DOM 更新后同步执行,获取元素尺寸
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
setHeight(rect.height);
}, []);
dart
// Flutter ------ 帧渲染完成后执行,获取 Widget 尺寸
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final box = _key.currentContext?.findRenderObject() as RenderBox?;
final size = box?.size;
setState(() => _height = size?.height ?? 0);
});
}
九、React.memo → const Widget
jsx
// React ------ 父组件重渲染时,props 没变就跳过
const Child = React.memo(({ title }) => <div>{title}</div>);
dart
// Flutter ------ 方式1:const(最简单,适合静态内容)
const MyWidget() // 编译期确定,父 build 重建时直接复用
// Flutter ------ 方式2:不依赖外部变化的子 Widget 天然不重建
// Flutter 的 diff 算法:类型相同 + key 相同 = 复用 State,不重新创建
十、整体对比:组件生命周期
scss
React 函数组件 Flutter StatefulWidget
─────────────────────────────────────────────────────────
渲染阶段:
function Component() ←→ Widget build(context)
挂载:
useEffect(() => {}, []) ←→ initState()
依赖更新:
useEffect(() => {}, [dep]) ←→ didUpdateWidget()
Context 变化:
useContext 自动订阅 ←→ didChangeDependencies()
卸载:
useEffect(() => () => {}) ←→ dispose()
每帧渲染后:
useLayoutEffect ←→ addPostFrameCallback
十一、常见陷阱对比
| 场景 | React 陷阱 | Flutter 等价陷阱 |
|---|---|---|
| 闭包捕获旧值 | useEffect 依赖数组漏写 |
didUpdateWidget 忘记判断新旧值 |
| 内存泄漏 | 忘记清理订阅 | dispose 里忘记 cancel/dispose |
| 死循环 | useEffect 内 setState 未加条件 |
build 里直接调用 setState |
| 异步 setState | 组件卸载后 setState | dispose 后调用 setState(会报错) |
| 不必要重建 | 未用 memo / useCallback |
未用 const / RepaintBoundary |