react函数式调用兄弟组件方法(一)

697 阅读4分钟

方法一: 使用useRef()和useImperativeHandle()

// 父组件 tsx
  const Roles:React.FC = () => {
  const event = useRef()
  return (
    < >
       <Addrole  triggerFn = { event }/>
       <RolesTable getChildFn = { event } />
    </>
  );
}
export default Roles;
// Addrole 子组件 tsx
//  如果用了typescript,props传递的参数或函数在子组件内需要定义类型,不然父组件会报错
const Addrole = (props: { triggerFn: any}) => {
  const { triggerFn } = props
    const triggerBroFn = () => {
        console.log("🚀 ~ file: addrole.tsx:41触发成功~ triggerBroFn:")
        triggerFn.current.getAllRoles()
    }
}
export default Addrole;
//  RolesTable 子组件 tsx
const RolesTable = (props:{getChildFn: any}) => {
const { getChildFn } = props
//暴露方法
useImperativeHandle(getChildFn,()=>({
  getAllRoles
}))}
export default RolesTable;

上面只是简单粗暴的使用,下面分析原理

细思极恐,上面的实现其根本原因是: 1. 父节点F将自己通过props分别传给了子组件A和B; 2. B在自身内部--暴露本应在F节点暴露的子节点B供外使用的某个函数---然而B在自己内部通过传递来的F暴露了自己的单个方法 3. A通过props拿到了F,从而调用了F暴露出来的B内部的函数

上面的实现原因(伪分析):

  1. 父组件定义了一个undefined的常规ref值 event: {current: undefined }
  2. 子组件RolesTable向绑定在自身身上的getChildFn变量,向外暴露了自身的getAllRoles方法,current = getAllRoles(原理不明,后续更新) 也就是useImperativeHandle时相当于把自己暴露给了父组件,把自身DOM暴露给父组件的event了
  3. 子组件读取current,此时不是undefined而是getAllRoles

根据官方文档分析

  1. 这里使用了官方的2个React Hook钩子函数,useRef()和useImperativeHandle()
  2. useRef有2个作用
  1. 定义一个不会随render渲染重置也不会触发render渲染的数据(可能类似localstorage?未实践,后续补充),其返回一个obj对象: {current: 你定义的值}
  2. 绑定一个jsx组件内的原生DOM节点,(不同于vue里的this.$refs,此处ref只能挂载到原生dom上,如果要挂载到组件,也是获取组件内部的原生节点,且必须给组件包裹forwardRef),同样放置在obj对象上: {current: 被绑定的原生节点信息}

这里用的是第2个作用,上面的绑定可以换成

// 父组件 tsx
  const Roles:React.FC = () => {
  const RolesTableDom = useRef(null)
  // 1.父组件这里可以直接调用RolesTableDom
  // 2. const RolesTableDom: null | {current: any} = useRef(null)
  // 3. setTimeout(() => { RolesTableDom.current && RolesTableDom.current.getAllRoles()}, 3000)
  return (
    < >
       {/*<Addrole  Addrole = { event }/>*/}
       <RolesTable ref = { RolesTableDom } />
    </>
  );
}
export default Roles;

当没有绑定原生节点时,可以useImperativeHandle暴露组件自身的函数,然后父组件就能获取到

//  RolesTable组件 tsx

const RolesTable = forwardRef((props:TempProps, ref) => {
//                这里的ref指向自己,return 返回出去了一个对象给current
    useImperativeHandle(ref,()=>({
      getAllRoles
}))}

再把父组件的useRef通过props传给AddRole子组件

// 父组件 tsx
  const Roles:React.FC = () => {
  const RolesTableDom = useRef(null)
  // 1.父组件这里可以直接调用RolesTableDom
  // 2. const RolesTableDom: null | {current: any} = useRef(null)
  // 3. setTimeout(() => { RolesTableDom.current && RolesTableDom.current.getAllRoles()}, 3000)
  return (
    < >
       <Addrole  getRolesTableDom = { RolesTableDom }/>
       <RolesTable ref = { RolesTableDom } />
    </>
  );
}
export default Roles;

Addrole子组件通过prop接收并触发

// Addrole 组件 tsx
const Addrole = (props: { getRolesTableDom: any}) => {
 const { getRolesTableDom } = props
    const triggerBroFn = () => {
      getRolesTableDom.current.getAllRoles()
    }
 }
export default Addrole;
  1. 再说useImperativeHandle,此hook函数主要作用是向外暴露公开自定义的数据
  1. 本来一般ref用法是绑定获取原生底层DOM节点的信息
import { useRef } from 'react';
export default function Form() {
  const inputRef = useRef(null);
  function handleClick() {
    inputRef.current.focus();
  }
  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>Focus the input</button>
    </>
  )}

2.参考官方文档,使用useImperativeHandle后,子组件将自己的信息暴露给父组件的useRef,且父组件只能获得子组件hook函数return暴露公开出来的内容--(思考:既可以是组件内绑定的原生dom,也可以是组件内自己的函数)

//  父组件
import { useRef } from 'react';
import MyInput from './MyInput.js';
export default function Form() {
  const ref = useRef(null);
  function handleClick() {ref.current.focus()}
  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}> Edit</button>
    </form>
  );
}
//  MyInput  子组件
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => {
    return {
      focus() {
        inputRef.current.focus();
      }}}, []);
  return <input {...props} ref={inputRef} />;
});
export default MyInput;

3.所以useImperativeHandle函数第一个参数,其实就是父组件的useRef,第二个参数是要暴露公开出去的数据,传递给父组件useRef的current

个人猜想: 此Hook其实就是把第二个参数与第一个merge, 参数一 : {父useRef: {current: undefined}} 然后赋值, 父useRef.current = 参数二

1.综上,最终实现原理其实就是,F父组件有个变量current,子组件B将自己的函数暴露挂载到current上,然后current作为prop传递给了子组件A,A就能调用了

2.总结: 官方建议useImperativeHandle尽量只用来对prop无法实现的函数进行操作,凡是能通过props实现的最好都用props

3.下一篇,任意调用其他组件方法,同时传递参数