【React Hooks】useRef 用法


接上一篇 【React Hooks】useState 用法博客提到的问题:

  • useRef 是一个对象,它拥有一个 current 属性,并且不管函数组件执行多少次,而 useRef 返回的对象永远都是原来那一个。
  • 所以我们可以使用useRef代替useState来解决那个问题
//useState 使用状态值,第一个 hook
//1.引入useState  钩子
import React, { useState, useRef } from 'react';
//计数器
function App() {
  const [count, setCount] = useState(0);
  const ref = useRef(0)
  const changeIncrementCount = () => {
    setTimeout(()=>{
		setCount(ref.current +=  1);
    },3000)
  }
  return (
    <div>
      <h2>计数器</h2>
      count:{count}
      <button onClick={changeIncrementCount}>+</button>
    </div>
  )
}
export default App;

useRef 介绍

  • useRef返回一个可变的ref 对象,其.current属性被初始化为传入的参数(initalValue
const refContainer = React.useRef(initalValue);
  • 特点:
1. useRef 是一个只能用于函数组件的方法。

2. useRef 是除字符串 ref、函数 ref、createRef 之外的第四种获取 ref 的方法。

3. useRef 在渲染周期内永远不会变,因此可以用来引用某些数据。

4. 修改 ref.current 不会引发组件重新渲染。

5. useRef 在每次重新渲染后都保持不变,而 createRef 每次都会发生变化
  • 示例:
import React, { useRef } from 'react';

function App() {
  //1.创建 ref对象
  let nameRef = useRef();//相当于createRef
  const mysubmit = () => {
    console.log("提交的信息是username:" + nameRef.current.value);

  }
  return (
    <div>
      <h2>useRef</h2>
      <input ref={nameRef} type="text" placeholder="请输入用户名" />
      <button onClick={mysubmit}>提交</button>
    </div>
  )
}
export default App;

useRef 用途

访问节点元素

访问 DOM 节点 或 React 元素

  • 尽管使用React时,我们推荐大家仅仅只关注数据,但也存在一些场景,我们需要去访问DOM节点才能达到目的
  • 通过点击别的按钮,让input组件获得焦点

import React, {Component, createRef} from "react";

export default class Demo extends Component {
  textInput = createRef<HTMLInputElement>();

  focusTextInput = () => {
    if (this.textInput.current) {
      this.textInput.current.focus();
    }
  }

  render() {
    return (
      <>
        <input type="text" ref={this.textInput} />
        <button onClick={this.focusTextInput}>点击我让input组件获得焦点</button>
      </>
    );
  }
}
  • 在函数组件中,我们可以通过useRef达到同样的目的

import React, {useRef} from "react";

export default function Demo() {
  const inputRef = useRef<HTMLInputElement>(null);

  const focusTextInput = () => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }

  return (
    <>
      <input type="text" ref={inputRef} />
      <button onClick={focusTextInput}>点击我让input组件获得焦点</button>
    </>
  );
}
  • 思考一个问题,默认支持的input组件拥有.focus方法,调用该方法,input组件就能够获得焦点。
  • 那如果我们自己要封装一个Input组件,并且也希望该Input组件能够拥有.focus.blur方法,我们应该怎么办?
  • 利用React提供的 api forwardRef就能够达到这个目的。forwardRef方法能够传递ref引用,具体使用如下
// 官网的案例
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
  • 我们也可以使用同样的方式,自定义Input组件。
import React, {forwardRef, useState, ChangeEvent} from 'react';

export interface InputProps {
  value?: string,
  onChange?: (value: string) => any
}

function Input({value, onChange}: InputProps, ref: any) {
  const [_value, setValue] = useState(value || '');

  const _onChange = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setValue(value);
    onChange && onChange(value);
  }

  return (
    <div>
      自定义Input组件
      <input value={_value} onChange={_onChange} ref={ref} />
    </div>
  );
}

export default forwardRef(Input);

useImperativeHandle可以让我们在使用ref时自定义暴露给父组件的实例值

  • 如果我们想要给.focus改个名字,或者返回其他额外的属性或者方法,我们可以使用useImperativeHandle

import React, {useRef, useImperativeHandle, forwardRef, Ref, useState, ChangeEvent} from 'react';

export interface InputProps {
  value?: string,
  onChange?: (value: string) => any
}

export interface XInput {
  focus: () => void;
  blur: () => void;
  sayHi: () => void
}

function Input({value, onChange}: InputProps, ref: Ref<XInput>) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [_value, setValue] = useState(value || '');

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current && inputRef.current.focus()
    },
    blur: () => {
      inputRef.current && inputRef.current.blur()
    },
    sayHi: () => {
      console.log('hello, world!');
    }
  }));

  const _onChange = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    console.log(value);
    setValue(value);
    onChange && onChange(value);
  }

  return (
    <div>
      自定义Input组件
      <input value={_value} onChange={_onChange} ref={inputRef} />
    </div>
  );
}

export default forwardRef(Input);
  • 使用一下这个Input组件
import React, { useRef, useState } from "react";
import Input from './components/Input';
import { Button } from "antd-mobile";

const Demo = () => {
  const textInput = useRef<any>(null);
  const [text, setText] = useState('')

  const focusTextInput = () => {
    if (textInput.current) {
      textInput.current.focus();
      textInput.current.sayHi();
    }
  }

  return (
    <>
      <Input ref={textInput} onChange={setText} value={text} />
      <Button onClick={focusTextInput}>点击我,input组件获得焦点</Button>
      <div>{text}</div>
    </>
  );
}

export default Demo;

保持可变变量

  • 在函数组件中,因为每次re-render就意味着函数重新执行一次,因此在函数内部保持变量引用是一件我们需要思考的事情
  • 这时候就可以利用 ref 的 .current
  • 一个很常见的应用场景就是对于定时器的清除。
  • 我们需要确保setInterval的执行结果timer的引用,才能准确的清除对应的定时器。
import React, { useRef, useEffect } from 'react';

export default function Timer() {
  const timerRef = useRef<NodeJS.Timeout>();

  useEffect(() => {
    timerRef.current = setInterval(() => {
      console.log('do something');
    }, 1000);

    // 组件卸载时,清除定时器
    return () => {
      timerRef.current && clearInterval(timerRef.current);
    }
  }, []);

  return (
    <div>
      // ...
    </div>
  )
}

注意:useRefuseState不同,如果一个状态或者数据会影响DOM的渲染结果,一定要避免使用useRef来保持引用

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
React Hooks 是 React 16.8 中新增的特性,它允许函数组件中使用 state 和其他 React 特性,从而使函数组件具有类组件的能力。 使用 React Hooks 需要先引入 ReactuseState、useEffect 等钩子函数,然后在函数组件中使用它们。 useState useState 是最常用的 Hook 之一,它可以让我们在函数组件中使用 state。useState 接收一个初始值作为参数,并返回一个数组,数组的第一个元素是当前 state 的值,第二个元素是更新 state 的函数。 例如,下面的代码在函数组件中使用了 useState 来保存一个计数器: ``` import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` useEffect useEffect 是另外一个常用的 Hook,它可以在函数组件中使用副作用。副作用包括数据获取、订阅或手动修改 DOM 等操作。useEffect 接收一个函数作为参数,该函数会在组件渲染完成后执行。 例如,下面的代码使用 useEffect 来更新页面标题: ``` import React, { useState, useEffect } from 'react'; function PageTitle() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` useContext useContext 可以让我们在函数组件中使用 Context。Context 是一种在组件树中传递数据的方法,它可以避免通过 props 层层传递数据。 例如,下面的代码使用 useContext 来获取全局的主题: ``` import React, { useContext } from 'react'; const ThemeContext = React.createContext('light'); function ThemeButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); } ``` 使用 useContext 前需要先创建一个 Context,可以使用 React.createContext 方法来创建。 除了上述三个 Hook,还有 useReducer、useCallback、useMemo、useRefHook 可以使用。使用这些 Hook 可以让函数组件更加强大和灵活。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗不甘坠落的流星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值