箭头函数不通知 redux 状态变化

Arrow function does not notify redux state changes

我在 React Native 中有一个 React 组件,我想手动处理 hardwareBackButton。 当我传递给 hardwareBackPressListener 的 backHandler 函数中的 redux 状态为真或假时,我有不同的行为。

const brandSelected = useSelector(state => state.map.brandSelected);

我的组件中有这个 useSelector 来访问状态。 我有 useEffect 函数来监视此状态的变化:(已正确工作并在状态变为真或假时记录状态。

React.useEffect(() => {
  console.log(brandSelected); // this is false correctly   
}, [brandSelected]);

最后我有一个 backHandler 函数,我将它传递给 hardwareBackPress Listener。

React.useEffect(() => {
  BackHandler.addEventListener('hardwareBackPress', backHandler);
  return () => {
    BackHandler.removeEventListener('hardwareBackPress', backHandler);
  };
}, []);

和 backHandler 函数:

const backHandler = () => {
  console.log('check, backhandler', brandSelected) // it logs true continuously
  if (brandSelected === true) {
    dispatch(
      dispatchItemToRedux({
        type: CATEGORIES_SELECTION,
        payload: {
          brandSelected: false,
        },
      }),
    );
    return true;
  }
  popScreen(Screens.Map);
  return true;
};

但是这个函数不会通知brandSelected 状态改变了。第一次它正常工作并分派函数并正确更改 redux 状态并且 useEffect 函数正确记录 false。但在其他尝试中它无法正常工作并且没有任何改变!!

这里的问题是您在初始渲染周期传递给 "hardwareBackPress" 事件侦听器的 backHandler 函数中 brandSelected 的陈旧外壳。 backHandler 函数仅具有初始渲染周期的值,永远不会 update/re-enclose 更新值。

要解决这个问题,您应该将 backHandler 状态值缓存在您可以在回调处理程序中引用的 React ref 中。

const brandSelected = useSelector(state => state.map.brandSelected);
const brandSelectedRef = React.useRef(brandSelected);

useEffect(() => {
  brandSelectedRef.current = brandSelected;
}, [brandSelected]);

...

const backHandler = () => {
  console.log('check, backhandler', brandSelectedRef.current)
  if (brandSelectedRef.current) {
    dispatch(
      dispatchItemToRedux({
        type: CATEGORIES_SELECTION,
        payload: {
          brandSelected: false,
        },
      }),
    );
    return true;
  }
  popScreen(Screens.Map);
  return true;
};

另一种方法是将 backHandler 移动到 useEffect 挂钩设置事件侦听器并使用 brandSelected 状态作为依赖项所以更新后的状态值被重新包含在回调中。

React.useEffect(() => {
  const backHandler = () => {
    console.log('check, backhandler', brandSelected)
    if (brandSelected) {
      dispatch(
        dispatchItemToRedux({
          type: CATEGORIES_SELECTION,
          payload: {
            brandSelected: false,
          },
        }),
      );
      return true;
    }
    popScreen(Screens.Map);
    return true;
  };

  BackHandler.addEventListener('hardwareBackPress', backHandler);
  return () => {
    BackHandler.removeEventListener('hardwareBackPress', backHandler);
  };
}, [brandSelected]);