1. 首页

从React官方文档看 refs 的使用和未来

React 先进的开发思想一直为社区所称道,基于数据流的设计极大地简化了前端开发成本。但如同官方文档所述,web 开发中有很多场景需求是脱离数据流的,典型的如处理文本输入框聚焦(focus)。为此 React 提供了 refs 供开发者使用。

笔者是在 2015 年下半年开始学习 React,那时候官方文档关于 refs 的介绍和使用与现在完全不同。最新的 React 版本(v15.5.4)已经对这个 API 进行了修改并更新,不过我们仍可以在文档中找到老版本 API 的蛛丝马迹:

从React官方文档看 refs 的使用和未来

我们来比较下新老 refs 有哪些异同:

在旧版本中,如上图所述,refs 的使用非常简单,因为每个组件实例都有一个this.refs属性,会自动引用所有包含 ref 属性组件的 DOM,所以我们只需要在目标组件上添加一个自定义的ref,然后进行使用即可:

class Button extends Component {
  constructor(props) {
    super(props);
  }

  componentDidMount = () => {
    let btn = this.refs.btn;
    let link = this.refs.link;
  };

  render() {
    return (
      <div>
        <button ref="btn">click</button>
        <a href="facebook.github.io/react" ref="link">
          click
        </a>
      </div>
    );
  }
}

文档加粗部分提醒到,将会在未来的某个版本把这种用法完全移除掉,建议开发者升级版本后使用新的 ref。那么新的 ref 如何使用呢?

第一个重点是将 ref 改为回调函数的方式去使用。

直接上代码:

class Input extends Component {
  constructor(props) {
    super(props);
  }

  focus = () => {
    this.textInput.focus();
  };

  render() {
    return (
      <div>
        <input
          ref={input => {
            this.textInput = input;
          }}
        />
      </div>
    );
  }
}

这里我们可能就有第一个疑问了,input参数是哪来的?文档中这样解释:

从React官方文档看 refs 的使用和未来

这就说明,当我们在 DOM Element 中使用ref时,回调函数将接收当前的 DOM 元素作为参数,然后存储一个指向这个 DOM 元素的引用。那么在示例代码中,我们已经把input元素存储在了this.textInput中,在focus函数中直接使用原生 DOM API 实现 focus 聚焦。

那么第二个疑问出现了,回调函数什么时候被调用?

答案是当组件挂载后和卸载后,以及 ref 属性本身发生变化时,回调函数就会被调用。

第二个重点是,可以在组件实例中使用`ref`。

前面的示例代码是在 DOM 添加ref属性,那么我们来看看如何在组件实例中使用。再上代码:


//<Input>来源于上面的示例代码👆 class AutoFocusTextInput extends Component { componentDidMount(){ this.textInput.focus(); } render(){ return ( <Input ref={(input) => { this.textInput = input }}> ) } }

当我们在<Input>中添加ref属性时,其回调函数接收已经挂载的组件实例<Input>作为参数,并通过this.textInput访问到其内部的focus方法。也就是说,上面的示例代码实现了当AutoFocusTextInput组件挂载后<Input>组件的自动聚焦。

接下来文档指出,<Input>组件必须是使用class声明的组件,不然无法使用。这意味着 React 逐渐与 ES6 全面接轨了。

第三个重点,不能在无状态组件中使用`ref`。

原因很简单,因为ref引用的是组件的实例,而无状态组件准确的说是个函数组件(Functional Component),没有实例。上代码:

function MyFunctionalComponent() {
  return <input />;
}

class Parent extends React.Component {
  render() {
    return (
      <MyFunctionalComponent
        ref={input => {
          this.textInput = input;
        }}
      />
    );
  }
}

上面的代码是无法正常工作的。

第四个重点,父组件的 ref 回调函数可以使用子组件的 DOM。

这是 Facebook 非常不推荐的做法,因为这样会打破组件的封装性,这种方法只是某些特殊场景下的权宜之计。我们看看如何实现,上代码:

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return <CustomTextInput inputRef={el => (this.inputElement = el)} />;
  }
}

原理就是父组件把ref的回调函数当做inputRefprops 传递给子组件,然后子组件<CustomTextInput>把这个函数和当前的 DOM 绑定,最终的结果是父组件<Parent>this.inputElement存储的 DOM 是子组件<CustomTextInput>中的input

同样的道理,如果 A 组件是 B 组件的父组件,B 组件是 C 组件的父组件,那么可用上面的方法,让 A 组件拿到 C 组件的 DOM。但是官方态度是 discouraged,这种多级调用确实不雅,我们确实需要考虑其他更好的方案了。

结语:

`refs`提供的是另一种与 react 传统响应数据流完全不同的组件间交互方式,所以官方指出不要过度使用`refs`,而且从官方对它的态度来看,未来或许有更好的 API 来取代它。但目前来说`refs`仍是一个不错的解决方案。

最近社区对于 React 的改进建议越来越多,例如this.setState()这样的回调函数到底是不是一个好方法,对于复杂程度高,数量多的组件如何高效地进行单元测试,大型应用对于大量 state 如何进行有效的管理,虽然有 redux,mobx 这样优秀的解决方案,但如果 react 从根本设计上解决这一痛点,是否能再次对前端开发进行新一轮技术革命呢?

今天是 2017 年 5 月 31 日,四年前的 5 月 30 日,React 正式发布了。过去的四年是 web 技术发展最快的四年,无数新技术和新思想喷薄而出。下个四年,我们共同期待。

作者:ssssyoki
链接:https://juejin.im/post/5927f51244d904006414925a

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:从React官方文档看 refs 的使用和未来

链接:https://www.javascriptc.com/3956.html

« Web 3.0 前瞻:基于区块链的下一代浏览器
技术人如何更好地把控发展趋势?»
Flutter 中文教程资源

相关推荐

QR code