1

前言

其实现依赖于:React + TypeScript
问题:如何将一个组件的方法暴露给其他组件呢?
NOTE:以下的方法都不是绝对的,可能会有更好的方式实现,又或者会有更多的方式去暴露子组件的 API,这里只是提供一个思路。

通过 ref

注:本节示例使用的是函数式组件 RefHook)的方式。

这其中有关基本知识如下:

  1. Hook useRef
  2. Hook useImperativeHandle
  3. React.forwardRef
// parent.tsx
import React, { useRef } from "react"
import Child from "./child"

const Parent = () => {
    const classChildRef: any = useRef(null)
    // 调用子组件实例上的 childGet()
    const getClassChildFn = () => classChildRef.current.childGet()
    return (
        <div>
            <Child ref={classChildRef} />
            <button onClick={getClassChildFn}>获取子组件值</button>
        </div>
    )
}
export default Parent
// child.tsx
import React, { useImperativeHandle, useRef } from "react"

// 使用 React.forwardRef() 将 ref 属性暴露(使得任意一个组件都可以使用该 ref),该函数本身返回一个 React 结点。
const Child = React.forwardRef(
    (props: any, ref: any) => {
        let state = { index: 0 }
        // 第 1 个参数:暴露 ref 属性。
        // 第 2 个参数:具体暴露的值。
        useImperativeHandle(ref, () => (
            {
                childGet() { console.log(state.index) },
            }
        ))
        return (<div>Child</div>)
    }
)

export default Child

当单击父组件按钮(获取子组件值)时,就会调用子组件实例使用 useImperativeHandle Hook 暴露出来的 childGet(),从而在控制台输出子组件实例的 state.index 值。

传递 props

 // parent.tsx
 import React from 'react';
 import Child from "./child";
 const Parent = () => (<Child sayHello={{ sayHello: "hello" }} />)
 export default Parent
 // child.tsx
 import {useEffect} from 'react';
 const Child = (props: any) => {
      const sayHello = (v: any) => { console.log(v) }
      // 当父组件有向子组件传递 props(sayHello) 时,就调用子组件的某个方法,或干脆调用父组件传递这个 props.
      useEffect(() => props.sayHello 
          ? sayHello(props.sayHello) 
          : console.error('sayHello is not passed'))
      return (<div className="child" />)
 }
 export default Child;

当在父组件使用子组件时,只需要向子组件传递约定的 props,那么子组件将按照某种方式对传递的 props 进行处理,或者说是工作。

Child 的静态属性

 // parent.tsx
 import React from 'react';
 import Child from "./child";
 const Parent = () => (<UseExport />)
 Child.say('Yomua'); // 控制台输出:Yomua
 export default Parent;
 // child.tsx
 import React from 'react';
 const Child = () => (<div>Child</div>)
 
 // 为 Child 定义静态属性
 Child.say = (v: any) => { console.log(v) }
 export default Child;

由于子组件存在静态属性,所以父组件中只要导入子组件,就可以直接使用:子组件.静态属性

实例化子组件

 // parent.tsx
 import React, { Component } from 'react';
 import Child from "./child"; 
 const ClassParent = () => {
      const getChildValue = () => {
          let child = new ClassChild("")
          child.childGet(); // 输出:0
      }
      return (
          <div>
              <ClassChild />
              <button onClick={getChildValue}>获取子组件值</button>
          </div>
      )
 }
 export default Parent
 // child.tsx
 import {Component} from "react"
 class ClassChild extends Component {
      static childGet: Function;
      state: { index: number, }
      constructor(props: any) {
          super(props)
          this.state = { index: 0 }
      }
      // 这个方法 可以被父组件获取到(只要父组件实例化子组件即可)
      childGet = () => { console.log(this.state.index) }
      render() {return (<div>Child</div>) }
 }
 export default ClassChild;

由于父组件需要实例化子组件,所以子组件最好使用 class component 的形式,或者使用函数式组件(除了箭头式的函数式组件除外)


yomua
14 声望3 粉丝

一个要努力学CS的萌新:)......