useRef
当想让组件记住一些信息,又不想触发新的渲染,可以使用ref:总是返回同一个对象。
1. state 和 refs 的比较
refs(普通的 JavaScript 对象) | state |
更改时不触发重新渲染 | 更改时触发重新渲染 |
可变的——修改/更新 (修改完立即生效
) | 不可变——必须使用state的setting函数去修改state变量 React 状态的限制(每个渲染的快照,不会同步更新) |
|
|
每次更新state,组件会重新渲染
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You clicked {count} times
</button>
);
}
ref.current
不要在渲染过程中读取或写入,组件不会随着每次ref增量而重新渲染:
import { useRef } from 'react';
export default function Counter() {
let countRef = useRef(0);
function handleClick() {
// This doesn't re-render the component!
countRef.current = countRef.current + 1;
}
return (
<button onClick={handleClick}>
You clicked {countRef.current} times
</button>
);
}
2. 常见使用场景
A. 存储timeout IDs
import { useRef } from 'react';
export default function Chat() {
let timeoutID = useRef(null);
function handleSend() {
timeoutID.current = setTimeout(() => {
alert('Sent!');
}, 3000);
}
function handleUndo() {
clearTimeout(timeoutID.current);
}
}
B. 存储和操纵dom元素(避免更改由 React 管理的 DOM 节点)
-
基础用法:
<div ref={counterRef} />
-
访问其他组件的dom节点:
import { forwardRef, useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
//(可选)限制公开的内容,不让Form组件的ref调用处理其他事件(如修改css)
const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({
// Only expose focus and nothing else
focus() {
realInputRef.current.focus();
},
}));
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
C. 存储其他对计算jsx非必要的对象