React 父组件获取子组件的方法/数据(useRef

帮同事debug发现的问题,记录一下,
弗曼学习大法好

interface RefFunType {
    hdlUpdate(): void,
    name: string
}

父组件使用useRef

useRef()

传入的泛型是子组件存放在ref.current中的数据格式,
(如果子组件内部绑定了自己的ref,那用的泛型为useRef

ref是绑定在自定义组件上,子组件直接从props中获取ref是获取不到的,
这是因为ref不是prop属性,会像key一样,被React进行特殊处理

所以需要使用

React.forwardRef((props: any, React.Ref | undefined) => {...})

将ref再重新转发到子组件中

在子组件中使用useImperativeHandle

useImperativeHandle(ref, () => ({
    [key:string]: any
}));

第一个param为转发的ref,第二个参数为一个fun,返回一个对象,这个对象就是可以在父组件中通过ref.current?.XXX获取到的方法和数据

在父组件中,需要注意的是,ref 对象内容发生变化时,useRef 并不会通知你,当.current改变时,组件并不会rerender。

因此,当我们需要在父组件中使用子组件的数据时,有两种方法。

  • 一种是在useEffect中将ref.current.XXX存放到state中,因为useEffect的调用是在组件完成渲染之后,也就是didMount之后才会调用,这个时候子组件已经渲染完成,内部的数据也已经构建完成,所以可以获取到内部数据
useEffect(() => {
    setName(childRef.current!.name)
})
  • 另外一种是使用回调ref,使用一个回调函数代替useRef
    (官方:使用 callback ref 可以确保 即便子组件延迟显示被测量的节点 (比如为了响应一次点击),我们依然能够在父组件接收到相关的信息,以便更新测量结果。)
const sexRef = useCallback(node => {
    console.log('node && node.name', node && node.name)
    node && setSex(node.name);
}, []);


而需要在父组件中调用子组件的方法的时候,我们可以使用这种方法


将子组件的方法嵌套多一层方法,如果不创建一个自己的方法的话,在第一次render的时候,current中的方法还没有创建完成,所以我们会将undefined传递给onClick。
而当我们创建了一个自己的方法,在内部去调用current.hdlUpdate的时候,此时调用的是current这个引用上的方法(把ref看成一个不会通知组件render,也不会重新创建的对象),此时hdlUpdate方法已经存在了。

一个比较具体的例子:

interface RefFunType {
    hdlUpdate(): void,
    name: string
}

// 子组件,使用forwardRef转发
const NameChild = forwardRef((props: any, ref: React.Ref | undefined) => {
    const [name, setName] = useState('jack');
    useImperativeHandle(
        ref,
        // 将需要的方法暴露出去
        () => ({
            hdlUpdate: () => {
                console.log('name', name)
            },
            name: 'name'
        })
    )
    return <>
        name:  setName(e.target.value)}>
    
});

// 另一个一样的子组件
const SexChild = forwardRef((props: any, ref: React.Ref | undefined) => {
    const [sex, setSex] = useState('man');
    useImperativeHandle(
        ref,
        () => ({
            hdlUpdate: () => {
                console.log('sex', sex)
            },
            name: sex
        })
    )
    return <>
        sex:  setSex(e.target.value)}>
    
})

interface SwitchComponentProps {
    Component: React.ReactNode
}
// 中间组件,用来测试每次调用的current是否是当前子组件的,和直接在父组件写if渲染一样
const SwitchComponent:FC = ({Component}) => {
    return 
{Component}
} // 父组件 const Index: FC = (props) => { const [type, setType] = useState(0); const [name, setName] = useState(""); // 创建ref const childRef = useRef(); const childrens = [ { component: }, { component: } ]; // 获取数据 useEffect(() => { setName(childRef.current!.name) }) // console.log('childRef.current', childRef.current) // const timer = setInterval(() => { // console.log('childRef.current', childRef.current) // }, 3000) // 在onClick绑定自己的方法,调用方法 return
{name}
}

这个是官方父组件调用子组件中的input的focus方法的例子

const FancyInput = forwardRef((props: any, ref: React.Ref | undefined) => {
    const inputRef = useRef(null);
    useImperativeHandle(ref, () => ({
      focus: () => {
        inputRef.current?.focus();
      }
    }));
    return ;
})

你可能感兴趣的:(react.js,html5,javascript,hooks)