首页 > 解决方案 > React:自定义钩子不适用于上下文

问题描述

我创建了一个自定义挂钩来将对象存储在 useState 挂钩中,并允许在不丢失其他条目的情况下更改属性。

const useObject = initialValue => {
  const [state, setState] = useState(initialValue);

  return [
    state,
    newState => {
      setState({
        ...state,
        ...newState
      });
    }
  ];
};

这个钩子在我的组件中有效,但在我将它分配给我的上下文时无效。

这是我所做的:

  1. 我创建了一个上下文:

export const navigation = createContext();

https://codesandbox.io/s/keen-glitter-3nob7?file=/src/store.js:40-83

  1. 我创建了一个 useObject 变量并将其作为值分配给我的 Context Provider

<navigation.Provider value={useObject()}>

https://codesandbox.io/s/keen-glitter-3nob7?file=/src/Layout.js:234-284

  1. 我通过 useContext 加载上下文并更改其值

const [navigationState, setNavigationState] = useContext(navigation);

https://codesandbox.io/s/keen-glitter-3nob7?file=/src/App.js:476-616

结果:

上下文始终存储新条目并删除所有现有条目。

任何人都知道为什么?

这是沙盒链接。您可以通过单击过滤器按钮对其进行测试。我希望将其{search:true, icon: 'times'}视为上下文值。谢谢!

https://codesandbox.io/s/keen-glitter-3nob7?file=/src/App.js

标签: reactjs

解决方案


这里有一件重要的事情需要注意。useEffect在 App.js 中运行一次,因此使用 setNavigationState 设置的 onClick 函数将在定义时使用其闭包中的值,即初始渲染。

因此,当您从上下文调用 Header.js 中的函数时,值以及 localState 将被重置为初始值。

解决方案 1:

这里的一种解决方案是使用回调方法进行状态更新。为此,您需要稍微修改您在 useObject 上的实现,以提供使用 setState 的回调值的能力

const useObject = initialValue => {
  const [state, setState] = useState(initialValue);

  return [
    state,
    newState => {
      if(typeof newState === 'function') {
        setState((prev) => ({ ...prev, ...newState(prev)}));
      } else {
      setState({
        ...state,
        ...newState
      });
    }
    }
  ];
};

然后在 onContextClick 函数中使用它

const onContextClick = () => {
    setState(prevState => {
      setNavigationState(prev => ({ icon: ICON[prevState.isOpen ? 0 : 1] }));
      return { isOpen: !prevState.isOpen };
    });
  };

工作演示

解决方案 2:

解决问题的另一种更简单的方法是使用useCallbackonContextClick 并使用 useEffect 更新导航状态,每次更新关闭状态时就像

 const onContextClick = React.useCallback(() => {
    setNavigationState({ icon: ICON[state.isOpen ? 0 : 1] });
    setState({ isOpen: !state.isOpen });
  }, [state]);

  useEffect(() => {
    setNavigationState({
      search: true,
      icon: ICON[0],
      onClick: onContextClick
    });
  }, [onContextClick]);

工作演示


推荐阅读