xzz2021
发布于 2024-02-03 / 1 阅读
0
0

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

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

方法一: 使用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内部的函数

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

父组件定义了一个undefined的常规ref值 event: {current: undefined }

子组件RolesTable向绑定在自身身上的getChildFn变量,向外暴露了自身的getAllRoles方法,current = getAllRoles(原理不明,后续更新) 也就是useImperativeHandle时相当于把自己暴露给了父组件,把自身DOM暴露给父组件的event了

子组件读取current,此时不是undefined而是getAllRoles

根据官方文档分析

这里使用了官方的2个React Hook钩子函数,useRef()和useImperativeHandle()

useRef有2个作用

定义一个不会随render渲染重置也不会触发render渲染的数据(可能类似localstorage?未实践,后续补充),其返回一个obj对象: {current: 你定义的值}

绑定一个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;

再说useImperativeHandle,此hook函数主要作用是向外暴露公开自定义的数据

本来一般ref用法是绑定获取原生底层DOM节点的信息

import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>


</>
)}

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 (





);
}
// 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.下一篇,任意调用其他组件方法,同时传递参数


评论