React---refs

Refs提供了一个访问render()方法内DOM节点或者ReactNode的方法

典型的React数据流中,props是父组件和子组件交互的唯一手段。要修改一个子组件,就需要使用新的props重新渲染它。然而,确实存在少数一些情况需要命令性地(imperatively)修改一个子节点而不是通过典型的props数据流方式。被修改的子节点可能是一个React组件实例(比如调用某个子组件的实例方法),亦或是一个DOM元素(比如手动地控制某个input标签聚焦)。对于这两种情况,React都提供了各种处理方法,refs就是其中的一种。

1. refs适用的场景

  • 手动控制DOM节点
  • 获取子组件的尺寸或者实例方法
  • 和第三方DOM库集成(典型的jPlayer)

注意:不要滥用refs,比如:在使用antd的<Modal />时,可以直接通过修改props.visible为true或false即可实现Modal组件的显示和隐藏,则大可不必使用该组件的show()、hide()方法

2. 创建Refs

注意:Ract 16.3引入的API React.createRef()。比较旧的React版本,保留了refs关键字。不管是新旧版本,都建议使用refs回调方式(最后的有对应的示例)。本文主要实现一个页面加载时,Input组件自动聚焦,并且在点击Button组件时聚焦Input组件的功能,使用的方法为React.createRef()。github代码库中有对应的新旧版本实现方式。

  1. refs通过React.createRef()创建,使属性ref附加到React元素上。Refs通常在 一个组件构造时赋值给一个实例属性,这样在整个组件中他们都可以被引用到。
  2. 创建以后,通过React.createRef().current方法获取。

根据节点类型的不同,ref的值也不同:

  • 如果ref用在HTML元素上,构造函数中通过React.createRef()创建的ref会将原生DOM元素放到它的current属性中。
  • 如果ref用在自定义组件类型上,ref使用它的current属性指向所挂载的组件实例。
  • 函数式组件上不能使用ref,因为它们没有实例。

3. DOM中创建与使用

class Input extends React.Component {
    constructor(props) {
        super(props)
        // 在构造方法内初始化
        this.inputRef = React.createRef()
    }

    componentDidMount() {
        // 使用.current调用
        this.inputRef.current.focus();
    }
    
    // Input的实例方法
    focus = () => {
        if(this.inputRef.current) this.inputRef.current.focus();
    }

    render() {
        return (
            <div className="block">
                <p>Input 加载时自动聚焦</p>
                <input ref={this.inputRef} />
            </div>
        )
    }
}

组件挂载时,React会将ref的current属性设置成DOM元素,卸载时,再把ref的current属性设置为null。ref更新发生在componentDidMount或者componentDidUpdate生命周期回调之前。

4. 自定义组件中创建与使用

import React from 'react'
import Button from './Button'
import Input from './Input'

class Ref extends React.Component {
    constructor(props) {
        super(props)
        // 初始化 获取挂载的组件Input实例
        this.inputComponentRef = React.createRef()
    }

    
    handleClick = () => {
        // 调用Input实例的方法
        if(this.inputComponentRef.current) this.inputComponentRef.current.focus()
    }

    render() {
        return (
            <div>
                <Button onClick={this.handleClick} />
                <Input ref={this.inputComponentRef} />
            </div>
        )
    }
}

export default Ref

同DOM中使用类似,组件挂载时,React会将ref的current属性设置成组件的实例,卸载时,再把ref的current属性设置为null。ref更新发生在componentDidMount或者componentDidUpdate生命周期回调之前。

5. 函数式组件无法为当前组件直接创建refs

const Input = () => <input />

class App extends React.Component {
  constructor(props) {
    super(props);
    this.inputComponentRef = React.createRef();
  }
  
  render() {
    // 不起作用,会报错
    return (
      <Input ref={this.inputComponentRef} />
    );
  }
}

但是,函数式组件内部可以使用ref引用属性使其指向一个DOM元素或者一个类组件,例如:

const Input = (props) => {
    let inputRef = React.createRef();

    function handleClick() {
      inputRef.current.focus();
    }

    return (
      <div>
        <input
         type="text"
          ref={inputRef} />

        <button
          onClick={handleClick}
        >Focus</button>
      </div>
    )
}

6. 使用回调的方式 (推荐)

在需要声明ref的位置绑定一个方法,返回的参数是DOM节点或则实例组件,组件在加载时会自动触发该回调方法,该参数作为实例的一个属性在其他位置直接使用即可。


class Input extends React.Component {
    constructor(props) {
        super(props)
    }

    componentDidMount() {
        // 不需要使用current调用
        this.inputRef && this.inputRef.focus();
    }

    initRef = (ele) => {
        // 组件加载时(或者更新时)自动触发该方法
        this.inputRef = ele
    }
    
    focus = () => {
        if(this.inputRef) this.inputRef.focus();
    }

    
    render() {
        return (
            <div className="block">
                <p>Input 加载时自动聚焦</p>
                <input ref={this.initRef} />
            </div>
        )
    }
}

export default Input

使用引用回调函数的注意事项
如果ref回调函数定义在内联函数(inline function)中,更新时他会被调用两次,第一次参数是null,第二次参数才是DOM元素。这是因为每个渲染都会创建一个新的函数实例,所以React需要清除旧的引用并设置新的。你可以通过将引用回调定义为该类的绑定方法来避免这种情况,但请注意,大多数情况下这样做或者不这样做都没太大关系。

7. React低版本遗留的API:字符串引用Refs

绑定一个 字符串类型的ref 属性到 render 的返回值上

<input ref="myInput" />

在其他位置(实例方法或者生命周期函数中)使用

componentDidMount() {
  // 保留关键字this.refs
  // 页面加载完成时使input标签自动聚焦
  this.refs.myInput.focus()
}

8. 建议使用其他的解决方案替代refs

在极少的一些情况下,我们需要从父组件中访问某个子DOM节点或者子组件的一些属性和方法。一般来说不建议这么做,因为它打破了组件封装,但是它偶尔也很有用,比如触发获取焦点,或者测量一个子DOM节点的尺寸或者位置。
本文代码链接地址:https://github.com/zhiyuanMain/ReactForJianshu.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • refs提供了可以在render方法中访问dom节点和创建的react元素的方法。 为了良好的阅读体验,请查看gi...
    mytac阅读 416评论 0 1
  •   在常规的 React 数据六中,props 是父组件与子组件交互的唯一方法。如果需要改子元素,你需要用新的pr...
    果汁凉茶丶阅读 964评论 0 0
  • It's a common pattern in React to wrap a component in an ...
    jplyue阅读 3,252评论 0 2
  • 有效的人生,无法违背的倾向性,在伦理道德法律范围内争取做更多,只有这辈子更好的自己,下辈子循环反复……
    心安_913c阅读 345评论 0 0
  • 2018年6月3日 星期日 天晴 文|墨凉 图|网络 我轻轻的告诉你,你今天真好看! 喂!你昨晚是失眠...
    十翰墨凉阅读 2,773评论 9 11