为什么 React 将函数引用检测为新的?

Why React detect the function reference as new?

我有一个关于自定义钩子的理论问题,涉及redux时的使用效果。 让我们假设我有这个代码:

//MyComponent.ts
import * as React from 'react';
import { connect } from 'react-redux';

const MyComponentBase = ({fetchData, data}) => {
   React.useEffect(() => {
      fetchData();
   }, [fetchData]);

   return <div>{data?.name}</data>
}
const mapStateToProps= state => {
   return {
       data: dataSelectors.data(state)
   }
}

const mapDispatchToProps= {
    fetchData: dataActions.fetchData
}

export const MyComponent = connect(mapStateToProps, mapDispatchToProps)(MyComponentBase);

这按预期工作,当组件呈现时它向服务器发出异步请求以获取数据(使用 redux-thunk)。它在 reduce 中初始化状态,并重新渲染组件。

但是,我们正在进行将此代码移至挂钩的迁移。我们稍微重构这段代码:

//MyHook.ts
import { useDispatch, useSelector } from 'react-redux';
import {fetchDataAction} from './actions.ts';

const dataState = (state) => state.data;
export const useDataSelectors = () => {
   return useSelector(dataState);
}

export const useDataActions = () => {
   const dispatch = useDispatch();

   return {
      fetchData: () => dispatch(fetchDataAction)
   };
};

//MyComponent.ts
export const MyComponent = () => {
   const data = useDataSelectors()>
   const {fetchData} = useDataActions();

   React.useEffect(() => {
      fetchData()
   }, [fetchData]);

  return <div>{data?.name}</data>
}

通过此更改,组件进入无限循环。当它第一次呈现时,它会获取数据。当数据到达时,它会更新存储并重新渲染组件。然而,在此重新渲染中,useEffect 表示 fetchData 的引用已更改,并再次进行提取,从而导致无限循环。

但我不明白为什么引用不同,钩子是在组件范围之外定义的,它们不会从 dom 或其他任何地方删除,因此它们的引用应该在每个组件上保持相同渲染周期。有什么想法吗?

useDataActions是一个钩子,但它一直在返回一个新的对象实例

return {
  fetchData: () => dispatch(fetchDataAction)
};

即使 fetchData 很可能是同一个对象,您还是将其包装在一个新对象中。

您可以 useStateuseMemo 来处理这个问题。

export const useDataActions = () => {
   const dispatch = useDispatch();
   const [dataActions, setDataActions] = useState({})

   useEffect(() => {
     setDataActions({
      fetchData: () => dispatch(fetchDataAction)
     })
   }, [dispatch]);

   return dataActions;
};

首先,如果您希望问题消失,您有几个选择:

  1. 使用 useCallback 钩子
  2. 使您的 fetchData 函数记忆化
  3. 不要在您的 useEffect 依赖项中使用 fetchData,因为您不需要它。你只需要在组件挂载时调用fetchData

所以这里是上面的变化: 1


export const useDataActions = () => {
   const dispatch = useDispatch();
   const fetchData = useCallback(() => dispatch(fetchDataAction), []);

   return {
      fetchData
   };
};

2第二种方法是:

export const MyComponent = () => {
   const data = useDataSelectors()>
   const {fetchData} = useDataActions();

   React.useEffect(() => {
      fetchData()
   }, []);

  return <div>{data?.name}</data>
}