关注这个标签,阅读本书所有文章
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
。
求点赞求分享求评论。由于文章推送算法调整,互动少不会推送到读者,互动一下表示认可,我会尽力保持高质量原创,万分感谢
原文始发于微信公众号(这波能反杀):这个 hook api,是 useState 的双生兄弟
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/240365.html