props/Ref | 青训营

79 阅读4分钟

props 是 React 组件通信最重要的手段 render 的过程,就是调用 React.createElement 形成新的 element 过程,新的 element 上就会有新的 props 属性,这个新 props 就是重新渲染视图的关键所在。

在标签内部的属性和方法会直接绑定在 props 对象的属性上,对于组件的插槽会被绑定在 props 的 Children 属性中。

React组件层级props作用 父组件props可以将数据给子组件去渲染,子组件可以通过props的callback向父组件传递信息

React更新机制中props作用 React中是无法直接检测出数据更新情况,所以要是有props来作为组件更新的准则

类组件中我们使用getDerivedStateFromProps来监听props变化 函数组件中我们用useEffect来监听props,但注意他会在页面初始化的时候执行一次

props children模式

① props 插槽组件

<Container>
    <Childern>
</Conatainer>

在Container中可以通过props.children属性访问到Children组件,是React element对象

② render props模式

<Container>
  { (ContainerProps)=> <Children {...ContainerProps}  /> }
  </Container>

function Container(props){
 const a = {
   name:'qzy'
 }
  return props.children(a)
}

可以将需要传给children的props直接用函数参数的形式传输

操作props

抽离props

function Son(props){
    console.log(props)
    return <div> hello,world </div>
}

function Father(props){
    const { age,...fatherProps  } = props
    return <Son  { ...fatherProps }  />
}
function Index(){
    const indexProps = {
        name:'alien',
        age:'28',
        mes:'let us learn React !'
    }
    return <Father { ...indexProps }  />
}

就是在父元素给子元素传递props的时候抽离出age

需要注意的是函数式子组件不能直接被父组件使用useRef获取实例,子组件必须通过forwardRef包裹, 并且内部使用useImperativeHandle封装返回的实例属性和方法

要克隆 FormItem 节点,将改变表单单元项的方法 handleChange 和表单的值 value 混入 props 中。 父元素Form在最后渲染前将child克隆并增添新的props助于后面与子组件的交互 最后return新的renderElement才是渲染出来的

之所以使用child.type.displayName是因为displayName是赋予组件类型,不是特定组件实例 父元素是不能通过child.displayName来访问 学习props的demo

Ref对象创建

①类组件React.createRef

class Index extends React.Component{
    constructor(props){
       super(props)
       this.currentDom = React.createRef(null)
    }
    componentDidMount(){
        console.log(this.currentDom)
    }
    render= () => <div ref={ this.currentDom } >ref对象模式获取元素或组件</div>
}

②函数组件 useRef


hooks 和函数组件对应的 fiber 对象建立起关联,

将 useRef 产生的 ref 对象挂到函数组件对应的 fiber 上,函数组件每次执行,只要组件不被销毁,函数组件对应的 fiber 对象一直存在,所以 ref 等信息就会被保存下来。

ref高阶用法

1 forwardRef 转发 Ref

forwardRef 接受了父级元素标记的 ref 信息,并把它转发下去,使得子组件可以通过 props 来接受到上一层级或者是更上层级的ref,

简而言之 forwardRef 的作用是允许你将一个 ref 传递给子组件。它解决了在函数组件中使用 ref 的问题,使得你可以在函数组件中像类组件一样访问子组件的 DOM 元素或实例。

默认情况下,函数式组件无法直接接收和传递 ref。使用 forwardRef,你可以创建一个能够接收 ref 的函数组件。而参数props包含了父组件传递下来的props和组件自身定义的props

函数组件 forwardRef + useImperativeHandle(可以直接返回给父组件方法

seImperativeHandle 一方面第一个参数接受父组件传递的 ref 对象,另一方面第二个参数是一个函数,函数返回值,作为 ref 对象获取的内容。一起看一下 useImperativeHandle 的基本使用。

  • 第一个参数 ref : 接受 forWardRef 传递过来的 ref 。
  • 第二个参数 createHandle :处理函数,返回值作为暴露给父组件的 ref 对象。
  • 第三个参数 deps :依赖项 deps,依赖项更改形成新的 ref 对象。

实例:

import React, { forwardRef, useImperativeHandle, useRef } from 'react';

// 子组件
const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  // 在子组件中定义的方法
  const focusInput = () => {
    inputRef.current.focus();
  };

  // 使用 useImperativeHandle 来将方法暴露给父组件
  useImperativeHandle(ref, () => ({
    focusInput
  }));

  return (
    <input ref={inputRef} type="text" placeholder="子组件输入框" />
  );
});

// 父组件
const ParentComponent = () => {
  const childRef = useRef(null);

  const handleButtonClick = () => {
    // 调用子组件暴露的方法
    childRef.current.focusInput();
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleButtonClick}>聚焦子组件输入框</button>
    </div>
  );
};

export default ParentComponent;

Ref好处


useRef 可以创建出一个 ref 原始对象,只要组件没有销毁,ref 对象就一直存在,那么完全可以把一些不依赖于视图更新的数据储存到 ref 对象中。这样做的好处有两个:

  • 第一个能够直接修改数据,不会造成函数组件冗余的更新作用。
  • 第二个 useRef 保存数据,如果有 useEffect ,useMemo 引用 ref 对象中的数据,无须将 ref 对象添加成 dep 依赖项,因为 useRef 始终指向一个内存空间所以这样一点好处是可以随时访问到变化后的值。