【问题标题】:React useEffect on function propsReact useEffect on function props
【发布时间】:2020-05-21 06:57:59
【问题描述】:

刚开始使用 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 数组的变化并被执行,但它不起作用。该函数被调用的唯一时间是最初在组件呈现时。

我做错了什么?

【问题讨论】:

  • Javascript 通过引用传递数组。即使你将一个新值推到它上面,它仍然是同一个数组。 useEffect 不进行深度比较(即不比较数组的内容)。
  • 如果不将 props.data 传递给您的 useEffect 依赖数组,请尝试传递 [JSON.stringify(props.data)] 并查看它是否按您想要的方式工作。顺便说一句,这可能不是一个很好的解决方案,只是说明正在发生的事情的一种方式。
  • 您可以尝试将数组复制到一个新数组中,而不是对其进行变异吗?即用data = [...data, obj]替换data.push(obj)
  • @ChristianFosli 这可能会导致useEffect 在每次渲染时重新运行,因为数组总是不同的。
  • 我们只是每 500 毫秒更改一次 data,对吧?我想我们希望 useEffect 在每次 data 更改时运行。

标签: javascript reactjs


【解决方案1】:

好的,我运行了您的代码并找出了问题所在。 data 正在发生变化,但不会触发 ChartWrapper 的重新渲染。因此,ChartWrapper 中的 useEffect 仅运行一次(在初始渲染时)。为了触发重新渲染,您需要使用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 函数被定义一次,因此其调用上下文中的 data 的值将始终是 data 的初始值(即空数组)。您可以通过在您的 newVal 函数中执行 console.log(data) 来看到这一点。要解决此问题,您也可以将 data 添加为 useEffect 的依赖项:

useEffect(() => {
  setInterval(newVal, 1000);
}, [data]);

但是每次数据更改时,您都会创建一个新的setInterval;你最终会同时运行其中的许多。您可以通过从 useEffect 返回一个函数来清理 setInterval,该函数将 clearInterval 它:

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 被调用时,它总是传递一个新的数组。所以 props.data 在 ChartWrapper 重新渲染时实际上是不同的。

【讨论】:

    【解决方案2】:

    钩子依赖中的对象通过引用进行比较。由于数组也是对象,所以当您说data.push 时,对数组的引用不会改变,因此不会触发挂钩。

    另一方面,原始值是按值进行比较的。由于 data.length 是一个原始类型 (number),因此出于您的目的,将依赖项放在 data.length 上就可以了。

    但是,如果您不修改数组的长度,只修改其中的值,那么触发引用差异的最简单方法(如第一段所述)是将这个数组包装在 setState 挂钩中.

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-29
      • 2020-11-22
      • 1970-01-01
      • 2021-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-23
      相关资源
      最近更新 更多