React16.3中的Refs和Forwarding Refs

简要介绍:React16.3中修改了Refs的使用方法,并且提供了Forwarding Refs用于在父组件中操作子组件中的dom节点,本文介绍一下新的Refs表示方法以及Forwarding Refs。

Ref的功能就不具体说了,就是在父组件中直接操作子组件的方法,Ref的方式,使其脱离了props传值,然后更新子组件的标准方法。

1 . React16.3中的Refs

(1)创建

16.3版本中Refs通过React.createRef()的方法创建,通过ref属性关联,比如我们创建一个Child子组件,并在该组件中使用Ref:

class Child extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();
    //React16.3中创建Ref的方法
  }
  render(){
    return <input ref={this.myRef}/>
  }
}

上述是创建Ref指向的方法,在Ref所指向的组件,在render后就可以调用,React16.3中提供了current属性,用于引用render后的节点:

  componentDidMount(){
    console.log(this.myRef.current);
    //render之后就可以输出该ref指向的那个节点
  }

此外,同样的Ref所指向的节点可以是dom节点,也可以是类组件。

(2)Ref属性指向的节点不能是函数组件

因为我们通过ref获得的组件,包含了声明周期和state,因此ref所指向的组件不可以是函数组件。

function MyFunctionComponent(){
  return <div>1</div>
}
class Child extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();
  }
  render(){
    return <MyFunctionComponent ref={this.myRef}/>
    //报错Warning: Stateless function components 
    //cannot be   given refs. Attempts to access this 
    //ref will fail.
  }
}

但是在函数组件中,是可以通过ref的方式指向一个类组件或者dom节点的。

2 .暴露子组件中的dom节点给父组件

(1)使用场景

在某些场景中, 我们可能需要在一个父组件中操作自组件中的dom节点,结构层级为:

父组件——>子组件——>子组件中的DOM节点

我们想在父组件中,直接操作子组件中的dom节点,这种操作不是很推荐,因为可能破坏了组件的封装性,标准的父子组件通信一般是通过Props传值的形式,但是有些时候有这种直接操作dom节点的需求,比如需要使dom节点聚焦,在父组件中需要知道子组件中某个dom节点的位置等等情况。

此时,如果我们仅仅在父组件中,在子组件的引用上,增加属性ref,那么这个ref仅仅指向的是子组件,而不能知道子组件中的dom节点,举例来说:

子组件为:

class Child extends React.Component{
  constructor(props){
    super(props);
  }
  render(){
    return <input />
  }
}

父组件中,ref:

class Father extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();
  }
  componentDidMount(){
    console.log(this.myRef.current);
  }
  render(){
    return <Child ref={this.myRef}/>
  }
}

输出的myRef为:

Child {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}

也就是说指向的是子组件,而不能指向子组件中的dom节点。

(2)React16.3中的Forwarding refs

通过React16.3中的Forwarding refs可以使得在父组件中可以得到子组件中的dom节点。

Forwarding refs中提供了一个React.forwardRef来创建组件,在React.forwardRef的方法中传递了参数ref,通过这个ref可以指向具体的某一个dom节点。具体的指向流程为:

父组件myRef——>React.forwardRef中的实参——>通过forwardRef方法创建的子组件中的ref——>指向子组件中的某一个dom节点

举例来说:

子组件(通过forwardRef方法创建)

const Child=React.forwardRef((props,ref)=>(
  <input ref={ref} />
));

父组件:

class Father extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();
  }
  componentDidMount(){
    console.log(this.myRef.current);
  }
  render(){
    return <Child ref={this.myRef}/>
  }
}

此时的myRef输出为:

input

我们一步步来看上述的过程是如何发生的。

  • 首先在Father组件中通过React createRef的方式创建一个myRef
  • 将myRef在子组件Child中通过制定ref={myRef}的形式传递下去
  • 在子组件的构造方法中,会接受传递过来的ref作为实参,然后这个实参的ref可以复制给子组件中的dom节点等。
  • 当父组件的myRef关联相应的dom节点之后,就可以通过current属性在父组件中得到子组件的dom节点。

(3)在高阶组件HOC中的Forwarding Refs

我们来创建一个简单的HOC:

function logProps(WrappedComponent) {
  class logProps extends React.Component {
    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return logProps;
}

这个HOC,用于构建一个新组件,在新组建中可以输出传入组件的所有属性信息。

我们令Child组件为:

class Child extends React.Component{
  constructor(props){
    super(props);
  }
  render(){
    return <input />
  }
}

我们在Child组件的基础上,通过logProps生成一个高阶组件:

const logProps=logProps(Child);

在父组件中通过ref获取子组件:

class Father extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();
  }
  componentDidMount(){
    console.log(this.myRef.current);
  }
  render(){
    return <LogProps ref={this.myRef}/>
  }
}

此时输出的是LogProps而不是Child,父组件的myRef指向的是HOC生成的组件。

LogProps {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}

同样的,如果我们想要父组件中的myRef指向HOC的原始组件,那么需要改写HOC的构造函数:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

此时,输出的myRef为:

Child

此外,虽然React中通过React.createRef()的方式来创建ref,但是并没有废除回调的方式来创建ref的方法。

猜你喜欢

转载自blog.csdn.net/liwusen/article/details/80009968