刚开始使用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数组中的更改并得到执行,但是它不起作用。该函数唯一被调用的时间是最初在组件呈现时。
我在做什么错了?
答案 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
没什么区别。