[英]React useEffect on function props
剛開始使用 React function hooks。 我正在研究一個案例,您有一個傳遞數組的父 function 和一個依賴於數組內容的子 function。
App.js(父級)->
import React, {useEffect} from 'react';
import ChartWrapper from './StaticChart/ChartWrapper';
import './App.css';
function App() {
let data = [];
function newVal() {
let obj = null;
let val = Math.floor(Math.random() * 10);
let timeNow = Date.now();
obj = {'time': timeNow, 'value': val};
data.push(obj);
// console.log(obj);
}
useEffect(
() => {
setInterval(() => {newVal();}, 500);
}, []
);
return (
<div className="App">
<ChartWrapper data={data} />
</div>
);
}
export default App;
ChartWrapper.js(子)->
import React, {useRef, useEffect} from 'react';
export default function ChartWrapper(props) {
const svgRef = useRef();
const navRef = useRef();
useEffect(
() => {
console.log('function called');
}, [props.data]
);
return (
<div>
<svg ref = {svgRef} width = {700} height = {400}></svg>
<svg ref = {navRef} width = {700} height = {75}></svg>
</div>
);
}
所以每次 App.js 在數組中添加一個新的 object 時,都會調用 ChartWrapper。 目前,在 ChartWrapper function 中,我有 useEffect 應該監聽 props.data 數組的變化並被執行,但它不起作用。 唯一一次調用 function 是最初在組件渲染時。
我究竟做錯了什么?
好的,我運行了您的代碼並找出了問題所在。 data
正在改變,但它不會觸發ChartWrapper
的重新渲染。 因此,您在useEffect
中的ChartWrapper
僅運行一次(在初始渲染時)。 為了觸發重新渲染,您需要使用useState
創建和更新data
:
let [data, setData] = useState([])
function newVal() {
let obj = null;
let val = Math.floor(Math.random() * 10);
let timeNow = Date.now();
obj = {'time': timeNow, 'value': val};
const newData = [...data]
newData.push(obj)
setData(newData)
}
然而,這還不夠。 由於您的setInterval
function 定義了一次,因此其調用上下文中的data
值將始終是data
的初始值(即空數組)。 您可以通過在newVal
function 中執行console.log(data)
來查看這一點。 要解決此問題,您也可以將data
作為依賴項添加到該useEffect
中:
useEffect(() => {
setInterval(newVal, 1000);
}, [data]);
但是,每次數據更改時,您都會創建一個新的setInterval
; 你最終會同時運行其中的許多。 您可以通過從useEffect
返回一個clearInterval
來清理 setInterval,這將清除它:
useEffect(() => {
const interval = setInterval(newVal, 1000);
return () => clearInterval(interval)
}, [data]);
但此時,您不妨改用setTimeout
:
useEffect(() => {
const timeout = setTimeout(newVal, 1000);
return () => clearTimeout(timeout)
}, [data]);
這將導致每次更改data
時都會創建一個新的setTimeout
,因此它將像您的setInterval
一樣運行。
無論如何,您的代碼將如下所示:
const ChartWrapper = (props) => {
useEffect(
() => {
console.log('function called');
}, [props.data]
);
return <p>asdf</p>
}
const App = () => {
let [data, setData] = useState([])
function newVal() {
let obj = null;
let val = Math.floor(Math.random() * 10);
let timeNow = Date.now();
obj = {'time': timeNow, 'value': val};
const newData = [...data]
newData.push(obj)
setData(newData)
}
useEffect(() => {
const timeout = setTimeout(newVal, 1000);
return () => clearTimeout(timeout)
}, [data]);
return (
<ChartWrapper data={data} />
)
}
還有一個運行示例: https://jsfiddle.net/kjypx0m7/3/
需要注意的是,這在 ChartWrapper 的useEffect
依賴項只是[props.data]
時運行良好。 這是因為當調用setData
時,它總是傳遞一個新數組。 所以當 ChartWrapper 重新渲染時, props.data
實際上是不同的。
鈎子依賴項中的對象通過引用進行比較。 由於 arrays 也是對象,因此當您說data.push
時,對數組的引用不會更改,因此不會觸發掛鈎。
另一方面,原始值是按值進行比較的。 由於data.length
是原始類型( number
),因此出於您的目的,將依賴項放在data.length
上就可以了。
但是,如果您不修改數組的長度,只修改其中的值,那么觸發引用差異的最簡單方法(如第一段中所述)是將這個數組包裝在setState
掛鈎中。
這是一個工作示例: https://codesandbox.io/s/xenodochial-murdock-qlto0 。 將 Child 組件中的依賴關系從[data.length]
更改為[data]
沒有區別。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.