当全局状态的叶值使用react钩子改变时,如何有条件地渲染组件

时间:2020-10-07 15:44:10

标签: reactjs react-hooks

我正在尝试创建一个钩子,该钩子允许组件订阅全局状态更改的一部分。例如,假设我的状态是这样的

{
   products: []
   userForm: {
     name: 'John Smith',
     dateOfBirth: '07/10/1991'
   }
}

仅当dateOfBirth字段更改时,才应重新呈现控制userForm中dateOfBirth字段的组件。

说我使用React上下文创建了一些全局状态。这是我尝试订阅该组件所关心的全局状态的领域

function useField(field) {
  const [globalState, setGlobalState] = useContext(GlobalState);
  const value = globalState[field] || "initial";
  const setValue = useCallback(
    (value) => {
      setGlobalState((state) => ({
        ...state,
        [field]: value
      }));
    },
    [setGlobalState, field]
  );
  return [value, setValue];
}

演示https://codesandbox.io/s/dawn-fog-ieqxs?file=/src/App.js:326-612

以上代码使使用useField钩子的所有组件重新呈现。

所需的行为是,仅当该字段更改时才应重新渲染组件。

1 个答案:

答案 0 :(得分:1)

它可以工作,但不能与Context API一起使用,因为目前Context API无法救助无用的渲染器。

换句话说:订阅上下文提供程序的组件将始终在提供程序值更改时呈现。

Context API已知问题的示例:


const GlobalContext = React.createContext(null);

const InnerComponent = () => {
  /* eslint-disable no-unused-vars */
  const { uselessState } = useContext(GlobalContext);
  console.log(`Inner rendered`);
  return <></>;
};

const InnerMemo = React.memo(InnerComponent);

const InnerComponentUsingContext = () => {
  const { counter, dispatch } = useContext(GlobalContext);
  console.log(`Inner Using Context rendered`);
  return (
    <>
      <div>{counter}</div>
      <button onClick={() => dispatch()}>Dispatch</button>
    </>
  );
};

const InnerComponentUsingContextMemo = React.memo(InnerComponentUsingContext);

const App = () => {
  const [counter, dispatch] = useReducer((p) => p + 1, 0);
  const [uselessState] = useState(null);

  return (
    <GlobalContext.Provider value={{ counter, uselessState, dispatch }}>
      <InnerMemo />
      <InnerComponentUsingContextMemo />
    </GlobalContext.Provider>
  );
};

Edit The Big Context Problem

也就是说,使用每个现代状态管理解决方案都具有救助功能,可以解决此问题:

// Always renders
const [globalState, setGlobalState] = useContext(GlobalState);
const value = globalState[field] || "initial";

// Bailout, for example with redux
const value = useReducer(globalState => globalState[field], /* Can add bailout function here if necessary */);