【发布时间】:2020-05-17 14:43:38
【问题描述】:
假设我像这样实现一个简单的全局加载状态:
// hooks/useLoading.js
import React, { createContext, useContext, useReducer } from 'react';
const Context = createContext();
const { Provider } = Context;
const initialState = {
isLoading: false,
};
function reducer(state, action) {
switch (action.type) {
case 'SET_LOADING_ON': {
return {
...state,
isLoading: true,
};
}
case 'SET_LOADING_OFF': {
return {
...state,
isLoading: false,
};
}
}
}
export const actionCreators = {
setLoadingOn: () => ({
type: 'SET_LOADING_ON',
}),
setLoadingOff: () => ({
type: 'SET_LOADING_OFF',
}),
};
export const LoadingProvider = ({ children }) => {
const [{ isLoading }, dispatch] = useReducer(reducer, initialState);
return <Provider value={{ isLoading, dispatch }}>{children}</Provider>;
};
export default () => useContext(Context);
然后假设我有一个组件会改变加载状态,但从不消耗它,如下所示:
import React from 'react';
import useLoading, { actionCreators } from 'hooks/useLoading';
export default () => {
const { dispatch } = useLoading();
dispatch(actionCreators.setLoadingOn();
doSomethingAsync().then(() => dispatch(actionCreators.setLoadingOff()))
return <React.Fragment />;
};
根据 useReducer 文档,dispatch 具有稳定的身份。我将此解释为,当组件从 useReducer 提取调度时,当连接到该调度的状态发生更改时,它不会重新渲染,因为对调度的引用将始终相同。基本上,dispatch 可以“像静态值一样对待”。
然而,当这段代码运行时,dispatch(actionCreators.setLoadingOn()) 行触发了对全局状态的更新,useLoading 钩子再次运行,dispatch(actionCreators.setLoadingOn()) 也是如此(无限重新渲染-_-)
我没有正确理解 useReducer 吗?还是我正在做的其他事情可能会导致无限重新渲染?
【问题讨论】:
-
doSomethingAsync可能是问题所在,因为它在每次渲染时都会重新运行。在大多数情况下,您希望用useEffect(() => {...}, [])包装doSomethingAsync,以防止它在每次渲染时重新运行。dispatch(actionCreators.setLoadingOn());也是如此。如果它没有包含在 useEffect 中,它将在每次渲染时调度setLoadingOn,这将导致重新渲染。此伪代码是否正确匹配您的实际问题,或者是否应该对其进行更新以更好地匹配现实与更多useEffects? -
您有语法错误。
setLoadingOn();不会关闭括号。 -
@Adam 当然是的。该组件主要用于演示目的。实际的 doSomethingAsync 将位于事件处理程序或 useEffect 之类的东西中。
-
@Adam 如果这是一个按钮,也许更现实更现实的例子是。可能是这样的:
onClick={() => dispatch(actionCreators.setLoadingOn())}抛开细节不谈,在高层次上,我们将拥有一个纯函数式组件,它可以改变某些状态。但是根据钩子的规则,像这样的组件会在每次状态更改时重新渲染,即使它没有订阅它改变的任何状态。当然,我可以使用useMemo之类的东西来控制这些组件的重新渲染规则,但仍然如此。看起来很奇怪
标签: javascript reactjs redux react-hooks use-reducer