reactjs - React函数式组件执行多次,useSelector()在每次函数式组件执行时执行多次

标签 reactjs react-hooks

我已经通读了有关钩子(Hook)的文档,但没有看到有关使用钩子(Hook)时组件生命周期的足够详细信息,因此我需要您的帮助。

更具体地说,我有一个包含以下相关问题的示例代码:https://github.com/khoitnm/practice-react-redux/tree/main/pro-00-old-react-version/src/comp-04-clickButton

问题 1) 我的功能组件使用了 3 个钩子(Hook):useSelector , useDispatch , useEffect ,但是为什么组件执行了 4 次(下面的代码和屏幕截图中有详细信息)? 据我理解,应该只有3次:

  1. 启动
  2. useSelector触发这次执行
  3. useEffect触发此执行。 useDispatch不会触发任何额外的执行,那为什么数据显示4次?

下面是我的代码和截图。 组件:

let componentCount = 0;
let returnCount = 0;
let useSelectorCount = 0;
let useEffectCount = 0;

const Comp04ClickButtonPage = (): JSX.Element => {
    componentCount++;
    console.log(`[${componentCount}] component - START ----------------------`);

    // useSelector
    const stringValueState = useSelector((rootState: RootState): string => {
        useSelectorCount++;
        console.log(`[${componentCount}] selectorCount: ${useSelectorCount}`);
        const result = rootState.comp04ClickButtonSlice.stringValue;
        return result;
    });
    console.log(`[${componentCount}] component - after useSelectorCount`);

    // useEffect
    const dispatch = useDispatch();
    useEffect(() => {
        useEffectCount++;
        console.log(`[${componentCount}] effectCount: ${useEffectCount}.`);
        dispatch(thunkComp04ClickButton(`${new Date().getTime()}`));
    }, [dispatch]);
    console.log(`[${componentCount}] component - after useEffect`);

    const onClickButton = () => {
        componentCount = returnCount = useSelectorCount = useEffectCount = 0; // reset counts
        console.log(`[${componentCount}] onClickNewValue`);
        dispatch(thunkComp04ClickButton(`Random ${new Date().getTime()}`));
    };

    // render
    returnCount++;
    return (
        <div>
            {console.log(`[${componentCount}] renderCount: ${returnCount}`)}
            <div>
                [{componentCount}], useEffect: {useEffectCount}, useSelectorCount: {useSelectorCount}, returnCount: {returnCount}
            </div>
            <button onClick={onClickButton}>Click Button</button>
        </div>
    );
}

砰的一声:

export const thunkComp04ClickButton = (stringValue: string): AppThunk => async (dispatch) => {
    try {
        console.log(`AppThunk: saveStringValue "${stringValue}" start dispatch`);
        dispatch(setComp04ClickButtonState({ stringValue: stringValue }));
        console.log(`AppThunk: saveStringValue "${stringValue}" end dispatch`);
    } catch (err) {
        console.error(`AppThunk: saveStringValue error dispatch` + err, err);
    }
};

切片:

type Comp04ClickButtonState = {
    stringValue: string;
};

const initialState: Comp04ClickButtonState = {
    stringValue: 'Init Comp04ClickButtonState',
};

const comp04ClickButtonSlice = createSlice({
    name: 'comp04ClickButtonSlice',
    initialState,
    reducers: {
        setComp04ClickButtonState(state, action: PayloadAction<Comp04ClickButtonState>) {
            console.log(`Slice: setComp04ClickButtonState "${action.payload.stringValue}"`);
            return action.payload;
        },
    },
});

export const { setComp04ClickButtonState } = comp04ClickButtonSlice.actions;
export default comp04ClickButtonSlice.reducer;

screenshot 1 :你会看到 componentCount 是 [4]returnCount: 4

问题2) 当组件被触发时,为什么useSelector执行多次?

更详细的,我们可以在控制台日志中看到: 组件第二次执行时,selectorCount从2次增加到5次(共4次)

[2] selectorCount: 2
[2] component - after useSelectorCount
[2] component - after useEffect
[2] renderCount: 2
[2] selectorCount: 3
[2] effectCount: 1.
AppThunk: saveStringValue "1610983429863" start dispatch
Slice: setComp04ClickButtonState "1610983429863"
[2] selectorCount: 4
AppThunk: saveStringValue "1610983429863" end dispatch
[2] selectorCount: 5

综上所述,当我打开那个网页时,最终的结果是:

  • useEffect:1(如预期)
  • componentCount = returnCount: 4(多次)
  • useSelector: 7(为什么这么多次???)

更新:正如@lawrence-witt 提到的,<React.StrictMode>导致对各种事物的双重调用,所以我更改了我的 index.tsx使用 <React.Fragment>像这样:

const render = () => {
    ReactDOM.render(
        <React.Fragment>
            <Provider store={store}>
                <App />
            </Provider>
        </React.Fragment>,
        document.getElementById('root')
    );
};

结果,组件现在只执行了 2 次。但是,正如您在 screenshot 2 中看到的那样,控制台日志仍然显示 useSelector第一次执行组件时触发4次。因此问题 2 仍然存在:为什么会触发这么多次?

[1] component - START ----------------------
[1] selectorCount: 1
[1] component - after useSelectorCount
[1] component - after useEffect
[1] renderCount: 1
[1] selectorCount: 2
[1] effectCount: 1.
AppThunk: saveStringValue "1611023432655" start dispatch
Slice: setComp04ClickButtonState "1611023432655"
[1] selectorCount: 3
AppThunk: saveStringValue "1611023432655" end dispatch
[1] selectorCount: 4

最佳答案

这里的问题是,当使用 useSelector Hook 时,它会在每次 Redux 管理的值更改时更新组件。您可以在此处阅读有关 useSelector 的更多信息:https://react-redux.js.org/api/hooks#useselector .

您添加到 useEffect 的依赖项可以删除,因为 dispatch 永远不会改变。 useDispatch 的官方文档可以在这里找到:https://react-redux.js.org/api/hooks#usedispatch .

// This will be called only once when the component is mounted for the first time
useEffect(() => {
   initDispatch();
},[]);
const initDispatch = useCallback(()=>{
  useEffectCount++;
  console.log(`[${componentCount}] effectCount: ${useEffectCount}.`);
  dispatch(thunkComp04ClickButton(`${new Date().getTime()}`));
}, [dispatch]);

dispatch 被调用时,这段代码仍然会重新渲染组件(因为 dispatchuseCallback 依赖数组中)这将强制useSelector 重新渲染组件。

关于reactjs - React函数式组件执行多次,useSelector()在每次函数式组件执行时执行多次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65777967/

相关文章:

javascript - 如何在 React 的 UseEffect() 中调用异步函数?

reactjs - Fetch 和 setInterval react 钩子(Hook)问题

javascript - 尝试使用在 React 中映射的输入字段将数据绑定(bind)到状态

javascript - 如何在类组件中使用useMediaQuery

reactjs - 使用react-router进行基于身份验证的重定向

node.js - redux onEnter 调度可以在服务器渲染上工作吗?

node.js - 找不到模块 : 'child process'

javascript - 我应该用 useCallback 或 useMemo 包装每个 prop,什么时候使用这个钩子(Hook)?

javascript - 尝试使用 map 映射一系列 url 图像以渲染到卡片

javascript - ReactJS如何在刷新页面时保持状态值