在这个简单的示例中,我使用 useMemo 记住Child组件,并将 callback 返回给父函数,以添加数据以响应钩子数组,该钩子数组最初设置为空
我的问题如下:为什么钩子 data 永不更改,并将其初始状态保留在来自记忆的Child组件的回调函数中。但是,在 useEffect 方法中检查 data 钩子或在渲染中使用该钩子时-它始终处于最新状态。
更多的是一个普遍的问题,幕后发生了什么,以及最好的方法是什么,例如,如果 data 始终具有初始状态,则从已记忆的子组件中检查回调函数中的重复值
我知道 useReducer ,可以在添加它们之前使用 hooks setter方法处理数据,但是也许还有其他方法吗?
父组件:
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>
},[])
}
答案 0 :(得分:2)
正如您所说,您的Child
组件已已存储,这意味着仅会在其依赖项之一发生更改时进行更新。
因此,在第一次渲染时,使用传递给Child
道具的函数创建callback
组件,此时data
是[]
。如果您单击按钮,则data
会正确更新,传递给callback
的{{1}} prop的函数也会正确更新,但是由于您没有为Child
设置任何依赖关系,您的useMemo
组件将不更新,并且仍会返回其已记录的版本,并收到其收到的第一个回调属性,该属性确实总是记录Child
的初始值:{{1} }。
因此,您所需要做的就是将data
添加到[]
的依赖项列表中:
callback
这样,当useMemo
属性更改时,您的export function Child({ callback }) {
return useMemo(() => {
return <button onClick={() => callback(1)}>Press!</button>;
}, [callback]);
}
组件还将更新其callback
事件处理程序。
此外,我建议使用eslint-plugin-react npm软件包,该软件包会立即警告您有关React挂钩中缺少依赖项的信息,并且更一般地是有关代码中不良做法的信息。
答案 1 :(得分:2)
有没有一种方法可以返回新的回调引用,而无需在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>
}, [])
}