React useEffect对功能道具

时间:2020-05-21 06:57:59

标签: javascript reactjs

刚开始使用React函数挂钩。我正在研究一种情况,您有一个传递数组的父函数和一个依赖于数组内容的子函数。

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在数组中添加新对象时,都会调用ChartWrapper。当前,在ChartWrapper函数中,我具有useEffect,它应该侦听props.data数组中的更改并得到执行,但是它不起作用。该函数唯一被调用的时间是最初在组件呈现时。

我在做什么错了?

2 个答案:

答案 0 :(得分:2)

好的,我运行了您的代码,找出了问题所在。 data正在发生变化,但并未触发ChartWrapper的重新渲染。因此,您在useEffect中的ChartWrapper仅运行一次(在初始渲染中)。为了触发重新渲染,您需要使用data创建和更新useState

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函数定义一次,因此data在其调用上下文中的值将始终为data的初始值(即为空数组)。您可以通过在console.log(data)函数中执行newVal来查看此信息。要解决此问题,您还可以将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]);

这将导致每次更改setTimeout时都会创建一个新的data,因此它将像您的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时,总是会传递一个新数组。因此props.data实际上在ChartWrapper重新渲染时有所不同。

答案 1 :(得分:1)

挂钩中依赖项的对象通过引用进行比较。由于数组也是对象,因此当您说BEGIN SELECT MAX(mlal_file_no) + 1 INTO ln_file_no FROM doc_mail_allocation WHERE mlal_file_year = P_FILE_YEAR AND std_file_type_code = 2 ; EXCEPTION WHEN no_data_found THEN ln_file_no := 1 ; WHEN OTHERS THEN p_error_code := 1 ; p_error_msg := 'ERROR 1520: File No:'||SQLERRM||' AND flal_file_year = '||P_FILE_YEAR; RETURN ; END ; 时,对该数组的引用不会更改,因此不会触发该挂钩。

原始值则通过值进行比较。由于data.push是原始类型(data.length),出于您的目的,将依赖项放在number上可以解决问题。

但是,如果您不修改数组的长度,仅修改数组中的值,触发引用差异的最简单方法(如第一段所述)就是将该数组包装在{{1}中}钩子。

这是一个有效的示例:https://codesandbox.io/s/xenodochial-murdock-qlto0。将Child组件中的依赖项从data.length更改为setState没什么区别。