You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter的useEffect中监听Redux Store状态变化?

Flutter Hooks + Redux:为何useEffect无法监听状态变化?

问题场景

我在使用flutter_hooks和flutter_redux开发组件,代码如下:

class Root extends HookWidget {
  const Root({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final store = StoreProvider.of<AppState>(context);

    store.onChange.listen((AppState appState) {
      print('store.onChange print: ${store.state.init}');
    });

    useEffect(() {
      print('useEffect print: ${store.state.init}');
      store.dispatch({'init': true});
    }, [store.state.init]);

    return Container();
  }
}

当前输出

useEffect print: false
store.onChange print: true

期望输出

useEffect print: false
store.onChange print: true
useEffect print: true

为什么更新Store状态时store.onChange能触发,但useEffect钩子却没有触发?


React中的实现参考

在React里可以轻松实现类似逻辑,代码如下:

import React from "react";
import { useAppDispatch, useAppSelector } from "../../app/hooks";
import { RootState } from "../../app/store";
import Box from "@mui/material/Box";
import { set } from "../../app/globalSlice";

const MyComponent = React.memo(() => {
  const dispatch = useAppDispatch();
  const init = useAppSelector((state: RootState) => state.globalState.init);

  React.useEffect(() => {
    console.log("init state: " + init);
    if (init === false) {
      dispatch(set({ init: true }));
    }
  }, [init]);

  return <Box />;
});

export default MyComponent;

React输出

init state: false
init state: true

可以看到useEffect会随着init状态变化自动执行两次,无需手动触发组件更新。Flutter里有没有同样的实现方式?


问题原因

核心问题在于Flutter的HookWidget不会自动订阅Redux状态变化:

  • React的useAppSelector内部会订阅Redux store,当选中的状态字段变化时,会触发组件重新渲染,进而让useEffect检测到依赖变化并执行。
  • 当前代码中,store.state.init的变化不会触发Root组件的build方法重新执行,useEffect的依赖列表自然不会被重新校验,也就不会再次执行。
  • 额外注意:Redux要求状态是不可变对象,如果AppState在更新时直接修改原对象属性而非返回新实例,也会导致状态变化无法被正确感知。

解决方案

方案1:使用Redux Hooks封装(推荐)

可以使用flutter_redux_hooks库,或自行封装useSelector钩子,让组件订阅Redux状态的指定字段,状态变化时自动触发组件重建:

class Root extends HookWidget {
  const Root({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 用useSelector订阅init状态,状态变化时组件自动重建
    final init = useSelector<AppState, bool>((state) => state.init);
    final store = StoreProvider.of<AppState>(context);

    useEffect(() {
      print('useEffect print: $init');
      if (!init) {
        store.dispatch({'init': true});
      }
    }, [init]); // 依赖init,变化时触发useEffect

    return Container();
  }
}

// 自行封装useSelector简化版(无需第三方库)
T useSelector<T>(T Function(AppState state) selector) {
  final store = StoreProvider.of<AppState>(useContext());
  final selectedState = useState(selector(store.state));

  useEffect(() {
    final subscription = store.onChange.listen((state) {
      final newSelected = selector(state);
      if (newSelected != selectedState.value) {
        selectedState.value = newSelected;
      }
    });
    return subscription.cancel;
  }, [store]);

  return selectedState.value;
}

方案2:手动在useEffect中监听store变化

如果不想引入第三方库或封装钩子,可直接在useEffect里监听store.onChange,在回调中处理状态变化逻辑:

class Root extends HookWidget {
  const Root({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final store = StoreProvider.of<AppState>(context);

    useEffect(() {
      print('useEffect print: ${store.state.init}');
      if (!store.state.init) {
        store.dispatch({'init': true});
      }

      // 监听store变化,触发后续逻辑
      final subscription = store.onChange.listen((appState) {
        print('useEffect 监听store变化: ${appState.init}');
        // 此处可添加状态变化后的处理逻辑
      });

      // 组件销毁时取消订阅
      return subscription.cancel;
    }, []); // 空依赖,只执行一次初始化

    return Container();
  }
}

关键注意事项

确保AppState是不可变对象:每次更新状态时,必须返回新的AppState实例,而非修改原对象属性。示例如下:

AppState reducer(AppState state, dynamic action) {
  if (action is InitAction) {
    // 返回新实例,而非直接修改state.init = true
    return AppState(init: true);
  }
  return state;
}

只有状态是不可变的,store.onChange才能正确捕获状态变化,组件也能感知到实例更新。


内容的提问来源于stack exchange,提问作者Lenny4

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:44:52