作用:
- 通过将
ref属性绑定到DOM节点或者组件中,来获取DOM节点,从而操作DOM。 - 在组件重新渲染时,保留变量的引用值
通过ref获取DOM节点
获取本组件的DOM节点
步骤一:引入hook
import { useRef } from 'react';
步骤二:在你的组件中使用它声明一个 ref
const myRef = useRef(null);
步骤三:将 ref 作为 ref 属性值传递给想要获取的 DOM 节点的 JSX 标签
<div ref={myRef}>
useRef Hook 返回一个对象,该对象有一个名为 current 的属性。最初,myRef.current 是 null。当 React 为这个 <div> 创建一个 DOM 节点时,React 会把对该节点的引用放入 myRef.current。然后,你可以从 事件处理器 访问此 DOM 节点,并使用在其上定义的内置浏览器 API。
示例一:组件内部只有一个ref
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>
聚焦输入框
</button>
</>
);
}
示例二:使用ref回调管理ref列表
import { useRef, useState } from "react";
export default function CatFriends() {
const itemsRef = useRef(null);
const [catList, setCatList] = useState(setupCatList);
function scrollToCat(cat) {
const map = getMap();
const node = map.get(cat);
node.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center",
});
}
function getMap() {
if (!itemsRef.current) {
// 首次运行时初始化 Map。
itemsRef.current = new Map();
}
return itemsRef.current;
}
return (
<>
<nav>
<button onClick={() => scrollToCat(catList[0])}>Tom</button>
<button onClick={() => scrollToCat(catList[5])}>Maru</button>
<button onClick={() => scrollToCat(catList[9])}>Jellylorum</button>
</nav>
<div>
<ul>
{catList.map((cat) => (
<li
key={cat}
ref={(node) => {
const map = getMap();
if (node) {
map.set(cat, node);
} else {
map.delete(cat);
}
}}
>
<img src={cat} />
</li>
))}
</ul>
</div>
</>
);
}
function setupCatList() {
const catList = [];
for (let i = 0; i < 10; i++) {
catList.push("https://loremflickr.com/320/240/cat?lock=" + i);
}
return catList;
}
获取子组件的DOM节点
借用forwardRef
import { forwardRef, useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
return (
<>
<MyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>
聚焦输入框
</button>
</>
);
}
使用ref回调来管理多个ref
import { forwardRef, useRef } from "react"
const Son = forwardRef((props, ref) => {
const list = [1, 2, 3, 4]
return <>
<ul>
{list.map(item => <li key={item} index={item}
ref={node => {
ref.current = ref.current || new Map()
ref.current.set(item, node)
}
}>{item}</li>)}
</ul>
</>
})
export default function App(){
const myRef = useRef(null)
const inputRef = useRef(null)
const clickBtn = index => {
const currentRef = inputRef.current.get(index)
console.log('当前dom:', currentRef)
currentRef.style.border = '1px solid red'
}
return <>
<input ref={myRef}/>
<button onClick={() => myRef.current.focus()}>使输入框聚焦</button>
<button onClick={() => clickBtn(1)}>获取第一个节点</button>
<button onClick={() => clickBtn(2)}>获取第二个节点</button>
<button onClick={() => clickBtn(3)}>获取第三个节点</button>
<Son ref={inputRef}/>
</>
}
只将子组件dom的部分api暴露给父组件(useImperativeHandle)
作用:防止父组件对子组件做一些意外的修改
import { forwardRef, useImperativeHandle, useRef } from "react"
const Son = forwardRef((props, ref) => {
const realRef = useRef(null)
useImperativeHandle(ref, () => ({
focus() {
realRef.current.focus()
}
}))
return <>
<input {...props} ref={realRef} />
</>
})
export default function App(){
const inputRef = useRef(null)
return <>
<button onClick={() => {
inputRef.current.focus()
console.log(inputRef.current);
}}>使输入框聚焦</button>
<Son ref={inputRef}/>
</>
}
通过Ref来保存变量的值
使用时机:当你希望组件“记住”某些信息,但又不想让这些信息 触发新的渲染 时,你可以使用 ref 使用方式:
// 可以传入任何类型的参数
const ref = useRef(0);
ref与state的对比:与 state 一样,React 会在每次重新渲染之间保留 ref。但是,设置 state 会重新渲染组件,更改 ref 不会!
| ref | state |
|---|---|
useRef(initialValue)返回 { current: initialValue } | useState(initialValue) 返回 state 变量的当前值和一个 state 设置函数 ( [value, setValue]) |
| 更改时不会触发重新渲染 | 更改时触发重新渲染。 |
可变 —— 你可以在渲染过程之外修改和更新 current 的值。 | “不可变” —— 你必须使用 state 设置函数来修改 state 变量,从而排队重新渲染。 |
你不应在渲染期间读取(或写入) current 值。 | 你可以随时读取 state。但是,每次渲染都有自己不变的 state 快照。 |