具有常量输入参数的反应钩子 - 钩子创建者?

React hook with constant input parameter - hook creator?

由于 React 挂钩依赖于执行顺序,因此通常不应在循环内部使用挂钩。我 运行 遇到过几种情况,在这些情况下,我对钩子有一个持续的输入,因此应该没有问题。我唯一想知道的是如何强制输入不变。

以下是一个简化的示例:

const useHookWithConstantInput = (constantIdArray) => {
  const initialState = buildInitialState(constantIdArray);
  const [state, changeState] = useState(initialState);

  const callbacks = constantIdArray.map((id) => useCallback(() => {
    const newState = buildNewState(id, constantIdArray);
    changeState(newState);
  }));

  return { state, callbacks };
}
const idArray = ['id-1', 'id-2', 'id-3'];

const SomeComponent = () => {
  const { state, callbacks } = useHookWithConstantInput(idArray);

  return (
    <div>
      <div onClick={callbacks[0]}>
        {state[0]}
      </div>

      <div onClick={callbacks[1]}>
        {state[1]}
      </div>

      <div onClick={callbacks[2]}>
        {state[2]}
      </div>
    </div>
  )
}

有没有一种模式可以强制 constantIdArray 不改变?我的想法是像这样为钩子使用创建者函数:

const createUseHookWithConstantInput = (constantIdArray) => () => {
  ...
}

const idArray = ['id-1', 'id-2', 'id-3'];

const useHookWithConstantInput = createUseHookWithConstantInput(idArray)

const SomeComponent = () => {
  const { state, callbacks } = useHookWithConstantInput();

  return (
    ...
  )
}

你是如何解决这种情况的?

一种方法是使用 useEffect 和一个空的依赖项列表,这样它只会 运行 一次。在这个里面你可以设置你的回调,之后它们将永远不会改变,因为 useEffect 不会再 运行 了。看起来像下面这样:

const useHookWithConstantInput = (constantIdArray) => {
  const [state, changeState] = useState({});
  const [callbacks, setCallbacks] = useState([]);

  useEffect(() => {
    changeState(buildInitialState(constantIdArray));
    const callbacksArray = constantIdArray.map((id) => {
        const newState = buildNewState(id, constantIdArray);
        changeState(newState);
    });

    setCallbacks(callbacksArray);
  }, []);

  return { state, callbacks };
}

虽然这会在第一次 运行 时设置两个状态而不是给它们初始值,但我认为这比每次挂钩 运行 时构建状态和创建新回调更好.

如果你不喜欢这条路线,你也可以像这样创建一个状态 const [constArray, setConstArray] = useState(constantIdArray); 因为给 useState 的参数只用作默认值,它会即使 constantIdArray 改变也永远不会改变。然后你只需要在钩子的其余部分使用 constArray 以确保它始终只是初始值。

另一种解决方案是 useMemo。这就是我最终实现的。

const createCallback = (id, changeState) => () => {
  const newState = buildNewState(id, constantIdArray);
  changeState(newState);
};

const useHookWithConstantInput = (constantIdArray) => {
  const initialState = buildInitialState(constantIdArray);
  const [state, changeState] = useState(initialState);

  const callbacks = useMemo(() =>
    constantIdArray.map((id) => createCallback(id, changeState)),
    [],
  );

  return { state, callbacks };
};