最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 梳理 React 中与 Ref 相关的一些知识点

    正文概述 掘金(周小亮)   2021-04-03   761

    梳理 React 中与 Ref 相关的一些知识点

    React 的官方文档中,分别对 Refs转发Refs & DOM 以及 Hooks 中的 UseRef()useImperativeHandle() 做了详细的介绍。

    因为这些章节比较分散,对于想要系统学习 Ref 的同学来说,可能阅读成本比较高昂。加上这些知识之间的贯通性,放在一起做比较可以加深对它的理解。

    接下来我会按照自己的理解组织文章脉络,有兴趣的小伙伴直接按顺序阅读即可,相信到最后会对 Ref 有新的认识。

    先来看下 ref 出现的背景。

    背景

    在典型的 React 数据流中(自上而下),在父组件中想要更改子组件,则必须更改父组件的 state , 从而更改子组件接收到的 props ,触发子组件重新渲染。

    但是在某些特殊情况下,我们想在父组件中直接更改子组件。被修改的子组件可以是一个 react 组件 也可以直接是一个 DOM 元素。如以下例子中,我想在页面初次渲染的时候,自动聚焦输入框:

    // 修改DOM元素
    class Parent extends React.Component {
        render() {
            return <input />;
        }
    }
    
    // 修改子组件
    class Child extends React.Component {
        render() {
            return <input />;
        }
    }
    class Parent extends React.Component {
        render() {
            return <Child />;
        }
    }
    

    显然,我们需要拿到 input 元素,并执行其 focus() 方法。

    ref 便是这一情况的一剂良方。

    想要使用 ref,首先我们需要创建它。在 React 中,创建 ref 有三种方式:React.createRef()回调refHook API: useRef()。我们先看第一种。

    React.createRef()

    function Parent(props) {
        const myRef = React.createRef();
        return <input ref={myref} />;
    }
    

    通过 React.createRef() 创建 ref。并通过 ref 属性,附加到 React元素.

    现在通过 myRef.current 便可以拿到 input 元素

    currentref 的一个属性,其值根据节点的类型而有所不同:

    1. ref 属性用于 HTML 元素时,其值就是这个元素。
    2. ref 属性用于自定义的 class 组件时,其值为组件实例。

    好了,我们来实现上面说到的聚焦输入框吧~

    // 修改DOM元素
    class Parent extends React.Component {
        myRef = React.createRef();
        componentDidMount() {
            this.myRef.current.focus();
        }
        render() {
            return <input ref={this.myRef} />;
        }
    }
    
    // 修改子组件
    class Child extends React.Component {
        myRef = React.createRef();
        focusInput() {
            this.myRef.current.focus();
        }
        render() {
            return <input ref={this.myRef} />;
        }
    }
    class Parent extends React.Component {
        myRef = React.createRef();
        componentDidMount() {
            this.myRef.current.focusInput();
        }
        render() {
            return <Child ref={this.myRef} />;
        }
    }
    

    需要注意的是,函数组件因为没有实例,所以不能将 ref 属性作用于函数组件。

    // 这段代码将不会生效,并且会报错
    function Child(props) {
        const myRef = React.createRef();
        const focusInput = () => {
            myRef.current.focus();
        };
        return (
            <div>
                <input ref={myRef} />
                <input onClick={focusInput} />
            </div>
        );
    }
    class Parent extends React.Component {
        myRef = React.createRef();
        componentDidMount() {
            // 报错
            this.myRef.focusInput();
        }
        render() {
            return (
                // Child因为不是class组件,所以ref不会生效
                <Child ref={this.myRef} />
            );
        }
    }
    

    不过有变通的方法,后面会有。我们继续看第二种方法 回调ref? ~

    回调 ref

    回调ref 使用函数的形式。这给函数可以接收 React组件实例 或者 HTML DOM 作为参数。所以它和 React.createRef() 一样,也可以作用于 class组件DOM

    我们将上面的例子改成用 回调ref 的方法。

    // 修改DOM元素
    class Parent extends React.Component {
        myRef = null;
        setInputRef = (element) => {
            this.myRef = element;
        };
        componentDidMount() {
            // 注意这里的myRef就是DOM对象,不再有myRef.current
            this.myRef && this.myRef.focus();
        }
        render() {
            return <input ref={this.setInputRef} />;
        }
    }
    
    // 修改子组件
    class Child extends React.Component {
        myRef = null;
        setInputRef = (element) => {
            this.myRef = element;
        };
        focusInput() {
            this.myRef.focus();
        }
        render() {
            return <input ref={this.setInputRef} />;
        }
    }
    class Parent extends React.Component {
        myRef = null;
        setInputRef = (component) => {
            this.myRef = component;
        };
        componentDidMount() {
            this.myRef && this.myRef.focusInput();
        }
        render() {
            return <Child ref={this.setInputRef} />;
        }
    }
    

    在修改子组件的例子中,我们在 Parent 中调用 Child 的方法,实现功能。这是因为我们通过 ref 获取到了子组件实例。但是,其实 回调ref 可以直接 获取子组件中的DOM节点.

    // 修改子组件
    function Child(props) {
        return <input ref={props.inputRef} />;
    }
    class Parent extends React.Component {
        myRef = null;
        setInputRef = (element) => {
            this.myRef = element;
        };
        componentDidMount() {
            this.myRef && this.myRef.focusInput();
        }
        render() {
            return <Child inputRef={this.setInputRef} />;
        }
    }
    

    在上面的例子中,将函数作为 props 传递给了子组件。现在 this.myRef 将直接是 input 元素。

    那既然这样,思考如下代码:

    // 修改子组件
    function Child(props) {
        return <input ref={props.inputRef} />;
    }
    class Parent extends React.Component {
        myRef = React.createRef();
        componentDidMount() {
            this.myRef.current.focusInput();
        }
        render() {
            return <Child inputRef={this.myRef} />;
        }
    }
    

    显然,会报错的~

    那如何在使用 React.createRef() 的情况下,也能够在父组件中直接获取子组件的 DOM 元素而不是组件实例呢?

    这就要说到前面卖的关子了。前面说过,有方法将 ref 作用于函数组件。同样是这个方法,可以获取到子组件里的 DOM 元素。这个方法就是 React.forwardRef()

    React.forwardRef()

    forward顾名思义,就是将我们创建的 ref 转发到子组件中的任意位置。来看一下它的用法。

    // Child
    const Child = React.forwardRef((props, ref) => <input ref={ref} />);
    
    export default Child;
    
    // Parent
    class Parent extends React.Component {
        myRef = React.createRef();
        render() {
            return <Child ref={this.myRef} />;
        }
    }
    

    Childref 是我们创建的 ref。这个 ref 将作为 React.forwardRef 的函数参数的第二个参数传递给 input , 作为其 ref 属性的值。这就是其透传的作用。

    上面说了两种创建 ref 的方法,以及直接获取子组件的 DOM 元素的方法。最后还有一种创建 ref 的方法属于 React Hook API: useRef()

    useRef()

    useRefReact.createRef 大同小异。都是用来创建 ref 对象。

    function test(props) {
        const myRef = useRef(null);
        useEffect(() => {
            myRef.current.focus();
        }, []);
        return <input ref={myRef} />;
    }
    

    然而,useRef 强大之处在于,其可以很方便的保存任何值。因为它本质上就是一个普通的 JS 对象。并且,无论组件如何重新渲染,useRef 都会返回同一个对象。

    我们来看一下它的巧用。

    function Test(props) {
        const ref = useRef(null);
        useEffect(() => {
            const id = setInterval(() => {});
            ref.current = id;
            return () => {
                clearInterval(ref.current);
            };
        });
        const clear = () => {
            clearInterval(ref.current);
        };
        return (
            <div>
                <button onClick={clear}>clear</button>
            </div>
        );
    }
    

    以上基本以及介绍完了关于 React Ref 的知识。还有一些不常用的没有介绍。我将官网链接按照逻辑顺序依次放在下面。想再系统地跟着官网过一遍的同学请可以按照这个顺序学习。

    1. React.createref

    这一章说明的 ref 出现的背景,为什么需要使用它。如何创建,以及在 DOM、class组件中使用。

    2. React.forwardRef

    这一章节主要介绍了 React.createRef() 的使用。如何利用它将我们创建的 ref 绑定到我们想要的任何子组件的任何DOM 元素上。

    3. Hook 中的 ref

    这里主要介绍了 useRef 和 useImperativeHandle 的用法。

    以上便是全部内容了,希望对你有所帮助,谢谢阅读~


    起源地下载网 » 梳理 React 中与 Ref 相关的一些知识点

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元