React进阶教程之Refs详解——通俗易懂,Class组件&Function组件(受控组件与非受控组件)

refs

「问题」

落日余晖🌞舒云剩影☁️的旁晚

小白:师父,React里面的Refs有什么用啊?

乐闻:先考你一个问题,现在有一个表单,页面加载完成时第一个input框自动获取焦点,不需要人为选中,应该怎么实现?

小白:直接给 input标签加上autoFocus属性呗,例如<input autoFocus="autofocus"/>

乐闻:emm... 这种方式也可以,还有其他的实现方式没?

小白:如果使用jquery的话就好了,可以直接获取到input标签实例,然后调用它的focus()方法就可以实现自动聚焦了。React的话不知道怎么弄。

乐闻:React也提供了我们访问DOM节点的能力,接下来我详细的跟你说说这个refs属性。

「官方定义」

Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

备注:1. DOM节点就是JSX里的div,input等原生标签。

  		2. React元素就是 我们创建的React组件,比如class组件,函数组件。

React推崇「状态决定视图」,页面怎么展示由数据状态控制。但是有些时候这样操作并不是很便捷,甚至不能实现理想功能。

React提供了Refs帮助开发者可以直接操作DOM节点,就像jquery时代一样,直接操作DOM,简单明了。但是官方建议:勿过度使用 Refs。 意思是能不用就不用,除非实在没有办法了。

「使用场景」

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

「代码实操」

React 组件分为两种 Class组件 + Function组件。两种组件使用Refs的方式也各不相同。

下面列表各种场景下的使用方式,如果举例没有覆盖到的,欢迎留言补充👏

  • 操作原生DOM

    • class组件
    1. React.createRef( 推荐
    import React, { Component } from "react";
    class RefsDeme extends Component {
      constructor(props) {
        super(props);
        this.state = {};
        this.inputRef = React.createRef();
      }
      componentWillMount() {
        console.log("componentWillMount->inputRef:", this.inputRef);
      }
      componentDidMount() {
        console.log("componentDidMount->inputRef:", this.inputRef);
        this.inputRef.current.focus();
      }
      render() {
        return (
          <div>
            姓名: <input ref={this.inputRef} />
          </div>
        );
      }
    }

    export default RefsDeme;
    React.createRef()
    1. 回调函数方式
    import React, { Component } from "react";
    class RefsDeme extends Component {
      constructor(props) {
        super(props);
        this.state = {};
        this.inputRef = null;
      }
      componentWillMount() {
        
      }
      componentDidMount() {
        this.inputRef.focus();
      }

      render() {
        return (
          <div>
            姓名:{" "}
            <input
              ref={(ref) => {
                this.inputRef = ref;
              }}
            />
          </div>
        );
      }
    }

    export default RefsDeme;
    回调函数方式
    1. string方式( 不推荐,了解即可
    import React, { Component } from "react";
    class RefsDeme extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }

      componentWillMount() {
        console.log("componentWillMount->inputRef:", this.refs.inputRef);
      }
      componentDidMount() {
        console.log("componentDidMount->inputRef:", this.refs.inputRef);
        this.refs.inputRef.focus();
      }

      render() {
        return (
          <div>
            姓名: <input ref='inputRef' />
          </div>
        );
      }
    }

    export default RefsDeme;
    image-20201110222021568
    • function组件

    因为Function组件不存在this,所以function组件不能通过class组件的方式来操作refs。Function组件使用refs,需要用到React Hooks。

    import React, { useRef, useEffect } from "react";

    export default function FunctionRef() {
      const inputRef = useRef();

      useEffect(() => {
        inputRef.current.focus();
      }, []);
      return (
        <div>
          FunctionRef: <input ref={inputRef} />
        </div>
      );
    }
    image-20201111110826638
  • 操作React组件

    • class组件
    // ClassComponent.js
    import React, { Component } from "react";

    export default class Class extends Component {
      onCheck = (type) => {
        alert("type:" + type);
      };
      render() {
        return <div onClick={this.onCheck.bind(this, "child")}>Class</div>;
      }
    }

    // RefsDemo.js
    import React, { Component } from "react";
    import ClassComponent from "./ClassComponent";
    class RefsDeme extends Component {
      constructor(props) {
        super(props);
        this.state = {};
        this.classRef = React.createRef();
      }
      componentWillMount() {
        console.log("componentWillMount->classRef:", this.classRef);
      }
      componentDidMount() {
        console.log("componentDidMount->classRef:", this.classRef);
      }
      render() {
        return (
            <ClassComponent ref={this.classRef} />
        );
      }
    }

    export default RefsDeme;

    image-20201111111523303

    备注:父组件可以获取自组件实例并调用自组件中定义的方法

    • function组件

    默认情况下,不能在函数组件上使用 ref 属性,因为它们没有实例

    直接对 普通的函数组件使用 ref 属性时会 Warning

    image-20201110223054314

    如果需要操作Function组件的refs,需要通过React.forwardRef高阶组件包裹一层。

    // FunctionComponent.js
    import React, { useImperativeHandle } from "react";

    export default React.forwardRef(function Function(props, ref) {
      const onCheck = (type) => {
        alert("type:" + type);
      };
      //需要把什么属性或者方法传给副组件,通过useImperativeHandle返回
      useImperativeHandle(ref, () => ({
        onCheck: onCheck.bind(null, "father"),
      }));

      return <div onClick={onCheck.bind(null, "child")}>Function</div>;
    });

    // RefsDemo.js
    import React, { Component } from "react";
    import FunctionComponent from "./FunctionComponent";
    import ClassComponent from "./ClassComponent";
    class RefsDeme extends Component {
      constructor(props) {
        super(props);
        this.state = {};
        this.functionRef = React.createRef();
      }

      componentWillMount() {
        console.log("componentWillMount->functionRef:", this.functionRef);
      }
      componentDidMount() {
        console.log("componentDidMount->functionRef:", this.functionRef);
      }

      render() {
        return (
         <div>
            <FunctionComponent ref={this.functionRef} />
            <button
              onClick={() => {
                this.functionRef.current.onCheck();
              }}>
              调用自组件方法
            </button>
          </div>
        );
      }
    }

    export default RefsDeme;
    image-20201111112045467

    点击自组件触发onCheck方法

    image-20201110225637217

    点击父组件按钮触发onCheck方法

    image-20201110225623260

总结ref提供了一种对于react标准的数据流不太适用的情况下组件间交互的方式, 但是在大多数情况下应该使用react响应数据流那种方式,不要过度使用ref。

普通人还在犹豫的时候,好看的人已经一键三连咯~👋👋👋

「公众号」:乐闻世界

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐闻x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值