当前位置 : 主页 > 网络编程 > JavaScript >

使用useImperativeHandle时父组件第一次没拿到子组件的问题

来源:互联网 收集:自由互联 发布时间:2023-02-08
目录 用useImperativeHandle父组件第一次没拿到子组件 背景需求 问题 解决方法 用useImperativeHandle让父组件操纵子组件的状态和方法 用useImperativeHandle父组件第一次没拿到子组件 背景需求 一
目录
  • 用useImperativeHandle父组件第一次没拿到子组件
    • 背景需求
    • 问题
    • 解决方法
  • 用useImperativeHandle让父组件操纵子组件的状态和方法

    用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的桥梁就这样就可以在父组件中控制子组件了,而不仅仅只是子随父渲染了。 

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持易盾网络。

    网友评论