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 始终指向一个内存空间,所以这样一点好处是可以随时访问到变化后的值。