这个 hook api,是 useState 的双生兄弟

简介: 这个 hook api,是 useState 的双生兄弟

使用函数创建组件,有一个非常特殊的地方。那就是当组件重新刷新时,组件函数会重新执行。于是在这种情况下,如何在函数内部持久化保存一个数据或者状态就变成了一个需要探讨的问题。


React 提供了一对双生兄弟 api 来解决数据持久化的问题:useState 与 useRef。

import {useState, useRef} from 'react'

通过上一章的学习我们知道,使用 useState 定义的数据会被监控,他们的变化会直接导致 UI 的变化。因此当我们在考虑需要持久化一个数据时,一定要区分清楚该数据自身的特性。


当该需要持久化的数据不会跟 UI 变化产生关系时,我们就需要用到 useRef。


useRef 是一个返回可变引用对象的函数。该对象 .current 属性的初始值为 useRef 传入的参数 initialValue


返回的对象将在组件整个生命周期中持续存在。

const ref = useRef(initialValue);


数据持久化


当一个数据需要在 re-render 的过程中持久稳定的保持该数据对应的状态时,我们可以考虑使用 useRef.


一个很常见的应用场景就是对定时器的操作。我们需要在恰当的时机开始或者停止或者卸载定时器的引用,那么准确的拿到定义定时器时的timer引用就非常关键。

import { useRef, useState } from 'react';
export default function Timer() {
  const [counter, setCounter] = useState(0)
  let timer = useRef<any>(null)
  function startHandle() {
    timer.current = setInterval(() => {
      setCounter(counter => counter + 1)
    }, 100)
  }
  function stopHandle() {
    clearInterval(timer.current)
  }
  return (
    <div>
      <div>{counter}</div>
      <button onClick={startHandle}>启动</button>
      <button onClick={stopHandle}>停止</button>
    </div>
  )
}

又比如我们上一章内容提到的保存请求参数。都可以用 useRef 来解决。


访问DOM节点或React元素


尽管使用 React 时,我推荐大家仅仅只关注数据,但也存在一些场景,我们需要去访问 DOM 节点才能达到目的。例如下面这个例子。

import {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>
    </>
  );
}

真实 DOM 元素的对象,其实也是一个需要持久化的对象,因此使用 useRef 来保存引用是非常合适的。


接下来思考一个问题,默认支持的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 {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);

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


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

import {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 { 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;

通过 ref 访问 DOM 节点,除了配合useRef之外,仍然可以使用回调的形式获取。

<input type="text" ref={(node) => input = node} />

但是在函数组件中,由于我们还要思考如何使用一个引用稳定的变量来关联节点,这会比直接使用useRef更麻烦。因此,函数组件中推荐优先使用useRef。

相关文章
|
测试技术 API Windows
使用钩子(Hook)实现Revit API中 PickObjects 完成按钮的触发
使用钩子(Hook)实现Revit API中 PickObjects 完成按钮的触发
使用钩子(Hook)实现Revit API中 PickObjects 完成按钮的触发
|
4月前
|
API
本地hook API MessageBoxA的masm32源代码[07-10更新]
本地hook API MessageBoxA的masm32源代码[07-10更新]
|
7月前
|
Java Linux API
微信API:探究Android平台下Hook技术的比较与应用场景分析
微信API:探究Android平台下Hook技术的比较与应用场景分析
|
8月前
|
存储 前端开发 JavaScript
这个 hook api,曾吓退许多前端开发者
这个 hook api,曾吓退许多前端开发者
|
JavaScript API
Vue3 —— 常用 Composition API(二)(hook 函数、toRef 和 toRefs)
Vue3 —— 常用 Composition API(二)(hook 函数、toRef 和 toRefs)
111 0
C/C++ HOOK 全局 API
全局 Hook 不一定需要用到 Dll ,比如全局的鼠标钩子、键盘钩子都是不需要 Dll 的,但是要钩住 API,就需要 Dll 的协助了,下面直接放上 Dll 的代码,注意这里使用的是 MFC DLL。
C/C++ HOOK 全局 API
|
8天前
|
JSON 前端开发 搜索推荐
关于商品详情 API 接口 JSON 格式返回数据解析的示例
本文介绍商品详情API接口返回的JSON数据解析。最外层为`product`对象,包含商品基本信息(如id、name、price)、分类信息(category)、图片(images)、属性(attributes)、用户评价(reviews)、库存(stock)和卖家信息(seller)。每个字段详细描述了商品的不同方面,帮助开发者准确提取和展示数据。具体结构和字段含义需结合实际业务需求和API文档理解。
|
2天前
|
JSON 搜索推荐 API
京东店铺所有商品接口系列(京东 API)
本文介绍如何使用Python调用京东API获取店铺商品信息。前期需搭建Python环境,安装`requests`库并熟悉`json`库的使用。接口采用POST请求,参数包括`app_key`、`method`、`timestamp`、`v`、`sign`和业务参数`360buy_param_json`。通过示例代码展示如何生成签名并发送请求。应用场景涵盖店铺管理、竞品分析、数据统计及商品推荐系统,帮助商家优化运营和提升竞争力。
39 23
|
2天前
|
JSON 缓存 API
解析电商商品详情API接口系列,json数据示例参考
电商商品详情API接口是电商平台的重要组成部分,提供了商品的详细信息,支持用户进行商品浏览和购买决策。通过合理的API设计和优化,可以提升系统性能和用户体验。希望本文的解析和示例能够为开发者提供参考,帮助构建高效、可靠的电商系统。
20 12
|
14天前
|
JSON API 数据格式
京东商品SKU价格接口(Jd.item_get)丨京东API接口指南
京东商品SKU价格接口(Jd.item_get)是京东开放平台提供的API,用于获取商品详细信息及价格。开发者需先注册账号、申请权限并获取密钥,随后通过HTTP请求调用API,传入商品ID等参数,返回JSON格式的商品信息,包括价格、原价等。接口支持GET/POST方式,适用于Python等语言的开发环境。
67 11

热门文章

最新文章