一、问题描述
封装了一个Chart组件,它接收一个boolean类型的props,根据这个boolean的true或false执行不同的操作。经过console.log验证,onReady函数只会在组件初次渲染时取到props值,不管后面的props变化成什么都无法重新取值。
二、代码描述
初始化状态为0,useRequest拿到后端的值为1。
传递给Chart组件的props,是可以拿到最新的,可以在40行的打印中看到。
但是onReady挂载的坐标轴点击事件打印出的却不会变化,依然是0。
javascript">javascript">// 父组件
import React, { useState, useEffect } from 'react';
import { useRequest } from 'ahooks';
const FatherComponent = () => {
const [flag, setFlag] = useState(0);
const { data, loding, run } = useRequest(
async (params) => {
try {
const { data, success, msg } = awiat getData();
if (!success) {
return [];
}
setFlag(data.flag)
return data;
} catch(e) { console.log(e) };
return [];
}, { manual: true })
useEffect(() => {
if (!isUndefined(data)) {
setFlag(data.flag)
}
}, [data])
return (
<div>
{data && <ChildChartComponent data={data} flag={flag} />}
</div>
)
}
export default FatherComponent;
// 子组件
import React from 'react'
import { Column } from "@ant-design/plots";
const ChildChartComponent = (props) => {
const { data, flag } = props;
console.log(flag)
const config = {
data,
// ...many config settings, it's unimportant
onReady: (plot) => {
plot.on("axis-label:click", (e) => {
if (Number(flag) === 1) {
console.log('执行props.flag为true的逻辑');
} else {
console.log('执行props.flag为false的逻辑');
}
})
}
}
return (
<Column {...config} />
)
}
export default ChildChartComponent;
三、原因解析
这个问题研究了整整一天,换了很多种方式都寻找不到问题所在。
一开始想是我得状态不对,再之后是觉得是最新的onReady没有被重新赋值给chart组件。
在官方文档中没找到相关问题描述,于是乎,我打开Github进入到组件仓库,在issues里找Bug和提问,终于被我找到跟我有相关问题的同志。
原来是因为onReady()函数是一个闭包,在子组件初次渲染的时候,它保存的值是初始值0,所以一直拿不到新状态。
四、修改子组件的写法,利用useRef解决闭包问题
我们声明一个ref,在effect中的deps校验规则设置为flag,effect方法内将ref的current每一次都指向flag。
将onReady中的on方法内的通过flag判断改为通过flagRef.current判断即可。
useRef可以解决闭包问题的原因在于: useRef 返回的是 { current: null },将对应数据赋值给 current,在声明之后,引用地址是不变的。
javascript">javascript">// 子组件
import React, { useRef, useEffect } from 'react'
import { Column } from "@ant-design/plots";
const ChildChartComponent = (props) => {
const { data, flag } = props;
const flagRef = useRef(null)
useEffect(() => {
flagRef.current = flag;
}, [flag])
const config = {
data,
// ...many config settings, it's unimportant
onReady: (plot) => {
plot.on("axis-label:click", (e) => {
if (Number(flagRef.current) === 1) {
console.log('执行props.flag为true的逻辑');
} else {
console.log('执行props.flag为false的逻辑');
}
})
}
}
return (
<Column {...config} />
)
}
export default ChildChartComponent;