blog icon indicating copy to clipboard operation
blog copied to clipboard

React useRef 实践指南

Open yangchch6 opened this issue 4 years ago • 0 comments

大家应该熟悉 React ref,它可以用来获取组件实例对象或者是DOM对象。而 useRef 这个 hooks 函数,除了传统的用法之外,还可以“跨渲染周期”保存数据。

useRef 传统用法

获取组件实例对象或者是DOM对象

import React, { useState, useEffect, useRef, useMemo } from 'react';

const Demo = () => {
    const [count, setCount] = useState(0);

    const doubleCount = useMemo(() => {
        return 2 * count;
    }, [count]); // 第二个参数为[]不会更新

    const couterRef = useRef();

    useEffect(() => {
        document.title = `The value is ${count}`;
        console.log(couterRef.current);
    }, [count]); // 第二个参数为[]不会更新
    
    return (
        <>
            <button ref={couterRef} onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
        </>
    );
}
export default Demo

代码中用 useRef 创建了 couterRef 对象,并将其赋给了 button 的 ref 属性。这样,通过访问 couterRef.current 就可以访问到 button 对应的 DOM 对象。

useRef 保存数据

在一个组件中,state 属性可以跨渲染周期,也就是在组件被多次渲染之后依旧不变。但是,state 的问题在于一旦修改了它就会造成组件的重新渲染。

而使用 useRef 来跨越渲染周期存储数据,对它修改是不会引起组件渲染的。

示例1: 保存定时器ID

import React, { useState, useEffect, useMemo, useRef } from 'react';

export default function App(props){
    const [count, setCount] = useState(0);

    const doubleCount = useMemo(() => {
        return 2 * count;
    }, [count]);

    const timerID = useRef();
    
    useEffect(() => {
        timerID.current = setInterval(()=>{
            setCount(count => count + 1);
        }, 1000); 
    }, []);
    
    useEffect(()=>{
        if(count > 10){
            clearInterval(timerID.current);
        }
    });
    
    return (
        <>
            <button onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
        </>
    );
}

在上面的例子中,我用 ref 对象的 current 属性来存储定时器的ID,这样便可以在多次渲染之后依旧保存定时器ID,从而能正常清除定时器。

示例2: 保存上一次的值

import React, { useState, useEffect, useRef } from 'react';

function usePrevious(value){
    const ref = useRef()
    useEffect(() => {
        ref.current = value
    })
    return ref.current
}

const Counter = () => {
    const [count, setCount] = useState(0)
    const preCount = usePrevious(count)
    useEffect(() => {
        setTimeout(() => setCount(10), 1000)
    })
    return <h1> Now: {count}, before: {preCount}</h1>
}

export default Counter

上面代码中,使用了 ref 对象的 current 属性来存储 count 被修改前的值

yangchch6 avatar Aug 26 '20 07:08 yangchch6