React 中的 Refs(9)

Refs

允许我们访问真实 DOM

一般的,React 数据流是通过 props 来实现父子组件的交互

Refs 允许我们用于强制修改子组件

// 输入框焦点
class MyInput extends React.Component {
  constructor(props) {
    super(props);

    this.inputRef = React.createRef();
  }

  inputOperating() {
    console.log(this.inputRef.current.value);
    this.inputRef.current.focus();
  }

  render() {
    return (
      <div>
        <input type="text" ref={this.inputRef} />
        <button onClick={this.inputOperating.bind(this)}>获取焦点</button>
      </div>
    );
  }
}

// 媒体管理
class MyVideo extends React.Component {
  constructor() {
    super();
    this.vdoRefs = React.createRef();
  }

  vdoPause() {
    this.vdoRefs.current.pause();
  }
  vdoPlay() {
    this.vdoRefs.current.play();
  }

  render() {
    return (
      <div>
        <video
          ref={this.vdoRefs}
          src="http://vjs.zencdn.net/v/oceans.mp4"
          controls
          autoPlay
          muted
        ></video>
        <button onClick={this.vdoPause.bind(this)}>暂停</button>
        <button onClick={this.vdoPlay.bind(this)}>播放</button>
      </div>
    );
  }
}
// 操作动画
class MyBox extends React.Component {
  constructor(props) {
    super(props);
    this.boxRef = React.createRef();
  }
  boxExtend() {
    const oBox = this.boxRef.current;
    oBox.style.width = "500px";
    oBox.style.height = "500px";
  }
  render() {
    return (
      <>
        <div
          ref={this.boxRef}
          style={{
            width: 200 + "px",
            height: 200 + "px",
            backgroundColor: "orange",
            transition: "all 1s",
          }}
        ></div>
        <button onClick={this.boxExtend.bind(this)}>Extend</button>
      </>
    );
  }
}

createRef 用法细节

  • 通过 createRef 创建 ref 对象
  • 通过元素的 ref 属性可以附加到 React 元素上
  • 一般通过构造器中给 this 赋值一个 ref,方便整个组件使用
  • ref 只要传递 React 元素中,就可以利用 refcurrent 属性访问到该真实 DOM 节点
  • ref 在 componentDidMountcomponentDidUpdate 触发前更新

ref 不同的使用方式:

  • ref 放在 HTML 元素上,则指向真实 DOM 节点
  • ref 放在 class 组件上,则指向组件实例
    <MyBox ref={xxx}/>
  • ref 放在函数组件上,不能增加 ref 属性,可以使用 React.useRef()

forwardRef (v16.3 以上版本)

将子组件的 ref 暴露给父组件

React.forwardRef((props, ref) => { return React 元素})
const MyInput = React.forwardRef((props, ref) => (
  // ref 参数只能在,用 forwardRef 定义的组件内可接收
  <input type="text" placeholder={props.placeholder} ref={ref} />
));

class App extends React.Component {
  constructor(props) {
    super(props);
    this.myInputRef = React.createRef();
  }
  inputOperate() {
    this.myInputRef.current.value = "";
    this.myInputRef.current.focus();
  }
  render() {
    return (
      <div>
        <MyInput placeholder="请输入" ref={this.myInputRef} />
        <button onClick={this.inputOperate.bind(this)}>获取焦点</button>
        {/* <MyVideo /> */}
        {/* <MyBox ref={xxx}/> */}
      </div>
    );
  }
}
// 高阶组件 ref 转发
class MyInput extends React.Component {
  render() {
    return <input type="text" placeholder={this.props.placeholder} />;
  }
}

function InputHoc(WarpperComponent) {
  class Input extends React.Component {
    render() {
      const { forwardRef, ...props } = this.props;
      return <WarpperComponent ref={forwardRef} {...props} />;
    }
  }
  function forwardRef(props, ref) {
    return <Input forwardRef={ref} {...props} />;
  }

  // 设置别名
  forwardRef.displayName = `Input - ${WarpperComponent.name}`;
  return React.forwardRef(forwardRef);


  // return React.forwardRef((props, ref) => (
  //   <Input forwardRef={ref} {...props} />
  // ));
}

const MyInputHoc = InputHoc(MyInput);

class App extends React.Component {
  constructor(props) {
    super(props);

    this.myInputRef = React.createRef();
  }

  componentDidMount() {
    console.log(this.myInputRef);
  }

  inputOperate() {
    this.myInputRef.current.value = "";
    this.myInputRef.current.focus();
  }

  render() {
    return (
      <>
        <MyInputHoc ref={this.myInputRef} placeholder="请输入" />
        <button onClick={this.inputOperate.bind(this)}>获取焦点</button>
      </>
    );
  }
}

Refs 转发(v16.2 及以下)

  1. 通过 props 转发
class MyInput extends React.Component {
  render() {
    return <input type="text" ref={this.props.inputRef} />;
  }
}

// 通过 props 转发
class App extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  componentDidMount() {
    console.log(this.inputRef);
  }

  render() {
    return (
      <div>
        <MyInput inputRef={this.inputRef} />
      </div>
    );
  }
}
  1. 回调 Refs
class App extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = null;
  }
  setMyInput(el) {
    this.inputRef = el;
  }
  focusInput() {
    this.inputRef.value = "";
    this.inputRef.focus();
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.setMyInput.bind(this)} />
        <button onClick={this.focusInput.bind(this)}>click</button>
      </div>
    );
  }
}
  1. 字符串 refs(不建议使用)
  • 组件实例下面的 refs 集合里的 ref
  • 需要 React 保持跟踪当前正在渲染的组件,this 没法确定
  • React 获取 ref 可能会比较慢
  • 不能在 render 中工作
  • 不能组合,只能设置一个 ref
class App extends React.Component {
  componentDidMount() {
    // 字符串 refs
    console.log(this.refs.inputRef);
  }

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

推荐阅读更多精彩内容