在这个简单的示例中,我使用useMemo记忆子组件并将回调传递回父函数以将数据添加到最初设置为空的 react 钩子数组。
我的问题如下:为什么钩子数据永远不会改变并将其初始状态保留在来自被记住的子组件的回调函数中。但是,在useEffect方法中检查数据挂钩或在渲染中使用该挂钩时 - 它始终具有最新状态。
这更像是一个普遍的问题,幕后会发生什么,以及最好的方法是什么,例如,如果数据始终具有初始状态,则检查来自记忆子组件的回调函数中的重复值。
我知道useReducer,我可以在添加钩子设置器方法之前操作数据,但也许还有其他方法?
父组件:
import React, {useEffect, useState} from 'react';
import Child from "./Child";
function App() {
const [data, setData] = useState([]);
useEffect(()=>{
console.log("UseEffect", data)
},[data]);
return (
<div>
<Child callback={(val)=>{
console.log("Always returns initial state", data); // <----------Why?
setData(old=>{
console.log("Return previous state", old);
return [...old, val]
})
}} />
Length: {data.length /*Always gets updated*/}
</div>
);
}
export default App;
子组件:在实际场景中,它是一个地图,我只想渲染一次,而不会导致重新渲染。
import React, {useMemo} from "react"
export default function Child({callback}) {
return useMemo(()=>{
return <button onClick={()=>callback(1)}>
Press!
</button>
},[])
}
没有办法返回回调的新引用,而无需在useMemo方法中添加依赖项
喜欢这个
// returns *(always the same)* function that will forward the call to the latest passed `callback`
function useFunction(callback) {
const ref = React.useRef();
ref.current = callback;
return React.useCallback(function() {
const callback = ref.current;
if (typeof callback === "function") {
return callback.apply(this, arguments);
}
}, []);
}
然后:
export function Child({ callback }) {
const handler = useFunction(callback);
return useMemo(() => {
return <button onClick={() => handler(1)}>Press!</button>;
}, []);
}
或
function App() {
const [data, setData] = useState([]);
const callback = useFunction((val) => {
console.log("Always returns the latest state", data);
setData([...data, val]);
});
return (
<div>
<Child callback={callback} />
Length: {data.length /*Always gets updated*/}
</div>
);
}
function Child({ callback }) {
return useMemo(() => {
return <button onClick={() => callback(1)}>
Press!
</button>
}, [])
}
正如您所说,您的Child
组件被记住,这意味着它只会在其依赖项之一发生变化时才更新。 因此,在第一次渲染时,您使用传递给callback
道具的函数创建Child
组件,此时,data
[]
。如果您单击该按钮,data
会正确更新,传递给 prop 的函数也会正确更新callback
Child
,但由于您没有将任何依赖项设置为useMemo
,您的Child
组件将不会更新,并且仍将返回其记忆版本,并收到第一个回调 prop,它确实总是记录data
的初始值:[]
.
因此,您所需要的只是将callback
添加到useMemo
的依赖项列表中:
export function Child({ callback }) {
return useMemo(() => {
return <button onClick={() => callback(1)}>Press!</button>;
}, [callback]);
}
这样,当callback
属性更改时,Child
组件也将更新其onClick
事件处理程序。
另外,我建议使用 eslint-plugin-react npm 包,它会立即警告你 React 钩子中缺少依赖项,更普遍地警告你代码中的不良做法。