React从入门到进阶之Refs&DOM以及Refs转发

什么是Refs

Refs提供了一种方式,运许我们访问DOM节点或在render方法中创建的React元素
在典型的React数据流中,props是父组件与子组件交互的唯一方式。要想修改一个子组件,我们需要使用新的props来重新渲染。但是在某些情况下,我们不得不在典型数据流之外去修改一些子组件。被修改的子组件可能是一个React组件的实例,也可能是一个DOM元素。这种情况下我们就必须要考虑其它方式了。好在React为我们提供了解决办法。

什么时候使用Refs

以下几种情况适合使用refs:

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

refs虽然能够让我们去操作DOM元素,但是在实际开发中不要过度使用refs,也就是说能不用refs的就尽量不要使用

Refs的使用

  • 创建Refs

Refs 是用React.createRef()来创建的,并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

class RefComponent extends React.Component{
constructor(props){
super(props);
this.myRef = React.createRef();
}
render(){
return <div ref={this.myRef} />
}
}
class RefComponent extends React.Component{
  constructor(props){
    super(props);
    this.myRef = React.createRef();
  }
  render(){
    return <div ref={this.myRef} />
  }

}
class RefComponent extends React.Component{ constructor(props){ super(props); this.myRef = React.createRef(); } render(){ return <div ref={this.myRef} /> } }
  • 访问Refs

访问ref的方式也很简单,当ref被传递给render中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。也就是说我们创建的ref会有一个current属性,那么我们所要操作的元素就存在这个current中。
ref的值根据节点的类型有所不同:

  • 当ref属性用于HTML元素时,构造函数中使用React.createRef()创建的ref接收底层DOM元素作为其current属性。
  • 当ref属性用于自定义Class组件时,ref对象接收组件的挂载实例作为其current属性。
  • 【注意】:我们不能在函数组件上使用ref属性,因为它们没有实例
    下面来看两个案例:
  • 为DOM元素添加ref
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
//创建一个ref来存储DOM元素
this.textInput = React.createRef();
}
focusTextInput = ()=>{
// 直接使用原生 API 使 text 输入框获得焦点
// 注意:我们通过 "current" 来访问 DOM 节点
this.textInput.current.focus();
}
render() {
// 告诉 React 我们想把 <input> ref 关联到
// 构造器里创建的 `textInput` 上
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
class CustomTextInput extends React.Component {
  constructor(props) {
      super(props);
      //创建一个ref来存储DOM元素
      this.textInput = React.createRef();
    }
    focusTextInput = ()=>{
      // 直接使用原生 API 使 text 输入框获得焦点
      // 注意:我们通过 "current" 来访问 DOM 节点
      this.textInput.current.focus();
    }
    render() {
      // 告诉 React 我们想把 <input> ref 关联到
      // 构造器里创建的 `textInput` 上
        return (
        <div>
          <input
            type="text"
            ref={this.textInput} />
          <input
            type="button"
            value="Focus the text input"
            onClick={this.focusTextInput}
          />
        </div>
    );
  }
}
class CustomTextInput extends React.Component { constructor(props) { super(props); //创建一个ref来存储DOM元素 this.textInput = React.createRef(); } focusTextInput = ()=>{ // 直接使用原生 API 使 text 输入框获得焦点 // 注意:我们通过 "current" 来访问 DOM 节点 this.textInput.current.focus(); } render() { // 告诉 React 我们想把 <input> ref 关联到 // 构造器里创建的 `textInput` 上 return ( <div> <input type="text" ref={this.textInput} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }

React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新。

  • 为 class 组件添加 Ref

如果我们想包装上面的 CustomTextInput,来模拟它挂载之后立即被点击的操作,我们可以使用 ref 来获取这个自定义的 input 组件并手动调用它的 focusTextInput 方法:

class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} />
);
}
}
class CustomTextInput extends React.Component {
// ...
}
class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }


  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

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

class CustomTextInput extends React.Component {
  // ...
}
class AutoFocusTextInput extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } componentDidMount() { this.textInput.current.focusTextInput(); } render() { return ( <CustomTextInput ref={this.textInput} /> ); } } class CustomTextInput extends React.Component { // ... }

需要注意的是:仅在 CustomTextInput 声明为 class 时才有效

Refs 与函数组件

默认情况下,我们不能在函数组件上使用 ref 属性,因为它们没有实例。也就是说我们自己定义的函数组件,当别人引用它并且想使用ref属性,这种是无效的。
如果非要在函数组件中使用 ref,可以使用 forwardRef,或者将该组件转化为 class 组件。

function MyFunctionComponent() {
return <input />;
}
class App extends React.Component{
constructor(props){
super(porps);
this.textInput = React.createRef();
}
render(){
//ref是无效的
return <MyFunctionComponent ref={this.textInput} />
}
}
function MyFunctionComponent() {
  return <input />;
}
class App extends React.Component{
  constructor(props){
    super(porps);
    this.textInput = React.createRef();
  }

  render(){
    //ref是无效的
    return <MyFunctionComponent ref={this.textInput} />
  }
}
function MyFunctionComponent() { return <input />; } class App extends React.Component{ constructor(props){ super(porps); this.textInput = React.createRef(); } render(){ //ref是无效的 return <MyFunctionComponent ref={this.textInput} /> } }

虽然不能在函数组件上使用ref,但是我们可以在函数组件内部使用ref,需要借助useRef() 钩子函数

function CustomTextInput(props) {
// 这里必须声明 textInput,这样 ref 才可以引用它
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
function CustomTextInput(props) {
  // 这里必须声明 textInput,这样 ref 才可以引用它
  const textInput = useRef(null);

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

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}
function CustomTextInput(props) { // 这里必须声明 textInput,这样 ref 才可以引用它 const textInput = useRef(null); function handleClick() { textInput.current.focus(); } return ( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }
  • Refs转发

Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。
下面的示例中,MyButton使用React.forwardRef来获取传递给它的ref,然后转发到它渲染的DOM button上。这样使用MyButton的组件可以获取到底层DOM节点button的ref,并在必要时访问,就像直接使用DOM button一样。

const MyButton = React.forwardRef((props, ref) =>{
(
<button ref={ref} className="my-button">{props.children}</button>
)
});
//可以直接获取DOM button的ref
const ref = React.createRef();
<MyButton ref={ref}>click me</MyButton>
const MyButton = React.forwardRef((props, ref) =>{
  (
    <button ref={ref} className="my-button">{props.children}</button>
  )
});


//可以直接获取DOM button的ref
const ref = React.createRef();
<MyButton ref={ref}>click me</MyButton>
const MyButton = React.forwardRef((props, ref) =>{ ( <button ref={ref} className="my-button">{props.children}</button> ) }); //可以直接获取DOM button的ref const ref = React.createRef(); <MyButton ref={ref}>click me</MyButton>

-上面说过,在函数组件上是不能使用ref的,那么这里却使用了ref那么是不是自相矛盾了呢?其实并不矛盾,细想:当我们在类组件或原生DOM元素上使用ref属性时,那么这个ref是直接与类组件或DOM元素绑定的,也就是说我们可以通过ref就能直接操作组件或者DOM元素。
而在这个示例中我们在函数组件上使用了ref,但不同的是:这个ref绑定的并不是当前函数组件本身,而是函数组件的子元素button
下面简单简单描述一下上面案例的执行步骤:

  • 1、我们通过调用React.createRef()创建了一个ref并将其赋值给变量ref
  • 2、指定ref为JSX属性,通过MyButton组件将其向下传递
  • 3、React 将ref作为参数传递给forwardRef函数,再通过该函数将其向下传递
  • 4、然后向下转发该ref参数到button上,同时将其指定为button的JSX属性
  • 5、当 ref 挂载完成,ref.current 将指向 < button> DOM 节点。
  • 6、最后我们就可以在外部直接访问到组件内部的元素了

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYXeGWLk' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片