簡體   English   中英

在 function 道具上反應 useEffect

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM