关于React的ref

ref的做用

咱们知道react数据流是自上而下的,想要改变子组件的状态只须要传递props,可是在某些特定场景下,咱们想获取子组件实例或者dom元素,按咱们本身的方式来操做元素的行为,这个时候就须要用到ref来获取子组件或者dom元素html

场景: 动画 或者 某个时候咱们 想focus 一个 input 输入框node

使用ref的三大原则:

1.能够在dom元素上面使用ref属性react

2.能够在class组件上面使用ref属性git

3.不许在函数组件上面使用ref属性,缘由:函数组件没有实例,父组件获取子组件的ref,实际上是在获取子组件的实例;(可是能够在函数组件中 获取子组件实例的ref,并用一个变量存储起来,这个是没有问题的)github

我的感受函数组件仍是有不少限制的,好比不能使用state和生命周期方法,也不能使用ref属性(这个问题我在开发中遇到了,详情见下文)api

基本使用

class App extends React.Component {
  constructor() {
    super();
    this.inputRef = React.createRef();
  }
  handleClick = () => {
    this.inputRef.current.focus();
  };

  render() {
    return (
      <div className="App"> <button onClick={this.handleClick}>点我 进行focus</button> <input ref={this.inputRef} /> </div> ); } } 复制代码

此处示例是react16.3+版本的使用(若是想使用旧版本请看下文),步骤有三个:数组

1.createRef方法 生成ref 对象app

2.render的时候 接收 子组件或者dom元素的ref属性less

3.用this.inputRef.current 来获取这个 子组件或者dom元素dom

回调的方式使用 ref

react16.2及如下通常用它

componentDidMount() {
    if (this.img && this.img.complete) {  //此处能够获取到 img 这个dom 元素
      this.onLoad()
    }
  }

  render() {
    const {className, src, alt, onClick, onRef, preview} = this.props

    return (
        {preview && !src ? (
          <div> <DefaultImageIcon /> </div>
        ) : (
          <img ref={node => { this.img = node if (onRef) { onRef() } }} src={src} alt={alt} onLoad={this.onLoad} onClick={onClick} /> )} ) } 复制代码

能够看到, 咱们也能够直接使用回调的方式来获取ref,其实我的感受这种方式更简单,可是至于做者为何放弃了这种方式,我目前还不清楚。~我以为是由于跨组件传递很麻烦吧~。 咱们还看到,代码中,onRef方法能够直接把 ref传给上层组件,那么,若是想使用 新版本api将 ref传递给上层组件,有什么办法呢? 请看 forwardRef

forwardRef

const MyInput = React.forwardRef((props, ref) =>  (
    <div>
      我是 自定义input 组件
      <input ref={ref}/>
    </div>
  ))

class App extends React.Component {
  constructor() {
    super();
    this.inputRef = React.createRef();
  }
  handleClick = () => {
    this.inputRef.current.focus();
  };
  render() {
    return (
      <div className="App">
        <button onClick={this.handleClick}>点我 进行focus</button>
        <MyInput ref={this.inputRef}/>
      </div>
    );
  }
}
复制代码

若是想在 上层组件中获取 下层组件内部的 dom 元素或者子孙组件实例,那么可使用 React.forwardRef 给组件包裹一层. 执行顺序是: 上层组件 createRef => App 组件传递 ref 给 MyInput => MyInput 经过 forwardRef 方法 接收到 ref => input 输入框 将节点 传给ref(此时这个ref已是上层组件的ref了)

高阶组件中 使用 ref

其实 forwardRef 使用到的场景很少, 可是有时候 确实须要跨组件传递 ref, 那就只能经过它来实现了。 咱们知道, 为了实现一些逻辑,咱们会使用高阶组件,它其实就是给一个组件封装一层,可是若是咱们想在上层组件获取ref,获取的是高阶组件的ref, 为何?举个 例子: 最上层组件 叫 App, 高阶组件叫 MyHOC, 子组件叫 MyInput, 咱们想在App 中 获取 这个MyInput, 那么以下使用是错误的:

以下例子,App里面接收到ref 实际上是Foo 的ref,然而 Foo 没有ref

// MyInput.js
import MyHOC from './MyHOC'
const MyInput = () =>  (
    <div> 我是 自定义input 组件 <input /> </div>
)
return MyHOC(MyInput)

// MyHOC.js
const MyHOC = (WrappedComponent) => {
   // do sth here..
    
  class Foo extends React.Component {
    render() {
      return <WrappedComponent  {...this.props} />;
    }
  } 
    return <Foo />;
} 
export default MyHOC

//App.js
import MyInput from './MyInput';
const ref = React.createRef();
<MyInput
  ref={ref} // 此处的 ref实际上是 Foo
/>;
复制代码

那么应该如何正确地在Hoc中使用呢?

// MyInput.js
import MyHOC from './MyHOC'
const MyInput = () =>  (
    <div> 我是 自定义input 组件 <input /> </div>
)
return MyHOC(MyInput)

// MyHOC
const MyHOC = (WrappedComponent) => {
  class Foo extends React.Component {
    render() {
      const { forwardedRef, ...rest } = this.props;
        // 此时 ref 即是这个WrappedComponent 
      return <WrappedComponent ref={forwardedRef} {...rest} />;
    }
  }
// 用React.forwardRef 包裹一层,这样将 上层组件的ref 命名为 forwardedRef (任意定义的一个属性名)  传递给子组件
  return React.forwardRef((props, ref) => {
    return <Foo {...props} forwardedRef={ref} />;
  });
}    
export default MyHOC

//App.js
import MyInput from './MyInput';
const ref = React.createRef();
<MyInput
  ref={ref}
/>;
复制代码

如上代码所示, 惟一一个不一样的地方是: MyHOC组件使用 React.forwardRef 将ref 向下层传递, 这样外层组件接收到的ref 就是真实的 MyInput 组件,而非 Foo了

问题&解决方法

  • 函数式组件不能使用ref属性(这个问题我在开发中也遇到了,使用ant design 的 getFieldDecorator()(myFnComponent)时 会报 “没法挂载ref的”错误)
<Form.Item
          label={field.label}
          key={`key_${field.label}`}
        >
          {render
            ? render()
            : getFieldDecorator(field.fieldName, {
                rules: R.pathOr([], ['config', 'rules'], field),
                initialValue: data[field.field],
              })(<Item {...field}/>)} // 此处的Item是个 函数式组件, 这样写会出错 // })(Item(field))} 这样写才是ok的, 由于Item会返回一个类组件 </Form.Item> // 报错信息: Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail. 复制代码
  • 组件中多个ref 怎么办? 那么就屡次建立 ref
  • 须要注意: 若是 在组件挂载 以前获取 this.myRef.current, 那么会返回一个 null, ref 会在 componentDidMount 或者 componentDidUpdate 以前更新

参考连接

gist.github.com/gaearon/1a0…

reactjs.org/docs/refs-a…

ant.design/components/…

reactjs.org/docs/forwar…

相关文章
相关标签/搜索