用useImperativeHandle父组件第一次没拿到子组件
背景需求
一个tab两个按钮A、B,默认选中的A,当点击到B时需要显示B对应的图表。
考虑到B的图表在页面加载时已经初始化完成,所以点击B时再调用图表的resize方法。
由于tab中的图表是写在子组件里,所以通过useImperativeHandle实现父组件调用子组件方法,React版本"react": "^18.1.0",代码如下
父组件:
const childRef = useRef() const item = [{ name: 'XXXX', content: <RunningRecord cRef={childRef} />, handClick: childRef.current?.resizeChart }] return <> …… <li onClick={() => { setTimeout(() => { console.log('~~item.handClick',item.handClick) item.handClick?.() }, 200) }} key={item.name}> {item.name} </li> …… <RunningRecord cRef={childRef} /> </>
子组件:
function RunningRecord({ cRef }) { …… useImperativeHandle(cRef,()=>({ resizeChart:()=> {dosomething……} }))
问题
这样写在本地开发模式中正常运行,但生产环境中父组件首次加载不能拿到子组件的方法,需tab切换到A再次且到B才行。
猜想原因,大概在生产环境中,父组件把子组件暴露出来的方法绑定到UI中的点击事件中,而子组件初始化的时机晚,初始完成后并没有把事件传回来。
这个猜想不一定准确,欢迎知道的小伙伴们补充。
解决方法
在父组件中,将子组件赋值的过程放在useEffect中,不写依赖项参数(不是没有依赖的空数组),再运行,一切正常。
const usageRecordData = [{ name: 'XXXX', content: <RunningRecord cRef={childRef} />, }] useEffect(() => { usageRecordData[1].handClick = childRef.current?.resizeChart })
用useImperativeHandle让父组件操纵子组件的状态和方法
useImperativeHandle是react官方为了简便我们的ref操作,同时还可以让子组件返回给父组件自身的状态和方法去调用
useRef将ref绑定到某个子组件标签上,用以获取整个子组件的方法和参数
useImperativeHandle
: 可以自定义暴露给父组件的方法或者变量
父组件
import React, { useState, useRef } from 'react' import ChildList from './ChildList' export default () => { let parentRef = useRef(null) const [name, setName] = useState('li') return <div> <ChildList parentRef={parentRef} name={name}></ChildList> <button onClick={() => { console.log("parentRef", parentRef) }}>获取子组件</button> </div> }
子组件
import React, { useImperativeHandle, forwardRef } from 'react' export default forwardRef((props, ref) => { console.log("ref", ref) useImperativeHandle(ref, () => { return { childFn } }) console.log(ref) const childFn = () => { console.log('子组件方法') } return <div> <div ref={ref} /> </div> })
用了ref的桥梁就这样就可以在父组件中控制子组件了,而不仅仅只是子随父渲染了。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持阿兔在线工具。