为什么 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
很可能是同一个对象,您还是将其包装在一个新对象中。
您可以 useState
或 useMemo
来处理这个问题。
export const useDataActions = () => {
const dispatch = useDispatch();
const [dataActions, setDataActions] = useState({})
useEffect(() => {
setDataActions({
fetchData: () => dispatch(fetchDataAction)
})
}, [dispatch]);
return dataActions;
};
首先,如果您希望问题消失,您有几个选择:
- 使用
useCallback
钩子 使您的 fetchData
函数记忆化
- 不要在您的
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>
}
我有一个关于自定义钩子的理论问题,涉及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
很可能是同一个对象,您还是将其包装在一个新对象中。
您可以 useState
或 useMemo
来处理这个问题。
export const useDataActions = () => {
const dispatch = useDispatch();
const [dataActions, setDataActions] = useState({})
useEffect(() => {
setDataActions({
fetchData: () => dispatch(fetchDataAction)
})
}, [dispatch]);
return dataActions;
};
首先,如果您希望问题消失,您有几个选择:
- 使用
useCallback
钩子 使您的 - 不要在您的
useEffect
依赖项中使用fetchData
,因为您不需要它。你只需要在组件挂载时调用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>
}