问题描述
需要在 React 组件或自定义 React 钩子内调用 React 钩子。它不能在循环内使用。考虑到这一点,请考虑以下情况:
const useListenToStateChange = (state,onEvent,key) => {
const value = state[key];
useEffect(() => {
if (value === null) {
onEvent(null);
} else {
onEvent(key,value);
}
},[value]);
};
const ExampleComponent = (props) => {
const { context,onError } = props;
const { state } = useContext(context);
useListenToStateChange(state,onError,KEY_ARRAY[0]);
useListenToStateChange(state,KEY_ARRAY[1]);
useListenToStateChange(state,KEY_ARRAY[2]);
useListenToStateChange(state,KEY_ARRAY[3]);
useListenToStateChange(state,KEY_ARRAY[4]);
useListenToStateChange(state,KEY_ARRAY[5]);
useListenToStateChange(state,KEY_ARRAY[6]);
useListenToStateChange(state,KEY_ARRAY[7]);
useListenToStateChange(state,KEY_ARRAY[8]);
useListenToStateChange(state,KEY_ARRAY[9]);
return <Text>{getTitle(state)}</Text>;
};
ExampleComponent
的返回值并不是真正的重点,所以可以忽略它。我担心的是 useListenToStateChange
被复制粘贴了十次,显然违反了 DRY 原则。
假设我们需要以这种方式调用 useListenToStateChange
——我们需要一个监听 state
返回的 useContext
对象的值,它在执行一个带有副作用的函数时发生更改(即,无法在更新 state
的减速器内调用回调;我们需要调用 useEffect
)。
这是柯里化派上用场的地方(注意:我使用 curry
中的 map
和 lodash/fp
):
const useListenToStateChange = _.curry((state,[value]);
});
const ExampleComponent = (props) => {
const { context,onError } = props;
const { state } = useContext(context);
const listenToState = useListenToStateChange(state,onError);
_.map(listenToState,KEY_ARRAY);
return <Text>{getTitle(state)}</Text>;
};
以上代码在 React Native 中是有效的,并且运行时不会抛出错误。
我喜欢它的读取方式,我喜欢这种方法使我们能够避免复制粘贴所有键。想象一下,如果您需要根据 KEY_ARRAY
数组设置多个侦听器。那将是一场噩梦。乍一看,我们也以这种方式遵循钩子规则:useListenToStateChange
是一个返回 JavaScript 函数的钩子,我们在 React 组件中调用该钩子。
但我正在思考的“问题”是,当你对一个函数进行柯里化时,显然它还没有执行。您必须提供最后一个参数才能运行它。这意味着 listenToState
在技术上是一个 React 钩子:当你调用它时,它会在 useEffect
内执行 useListenToStateChange
钩子。
然而,它不像钩子那样命名,因此绕过了 React 中说你必须在顶层调用它的检查。这意味着您可以映射 KEY_ARRAY
,并且由于它是一个预定义的非动态数组,因此保证在每次渲染时以相同的顺序调用钩子。因此,它避免了 React docs 中概述的问题。
所以我的问题是:这种对 React 钩子进行柯里化的使用是否违反了钩子规则?换句话说,这种模式是好的做法吗?
解决方法
这种对 React 钩子进行柯里化的使用是否违反了钩子规则?
没有。用柯里化没问题。
这种模式是好的做法吗?
我觉得没问题。
但是,我不建议从外部使用状态。您可以在该函数内使用状态:
const useListenToStateChange = (onEvent,key) => {
const { state } = useContext(YourContext);