- Published on
React ref相关
- Authors

- Name
- 李丹秋
使用Ref引用值
function useRef(initialValue) {
const [ref, unused] = useState({ current: initialValue })
return ref
}
useRef理论上可以使用useState实现 如果你的组件需要存储一些值,但不影响渲染逻辑,请选择ref
React Ref 操纵Dom
import { forwardRef, useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
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>
</>
);
}
最简单的方案是声明一个ref对象,将其挂载到对应的Dom元素上。这样就可以通过ref.current的方式获取到dom元素。 但是我们的本意只是想要调用focus方法,现在确直接暴露了dom,可以进行其他操作,可以用useImperativeHandle这个hooks,将子组件想要暴露的方法,直接传递给ref。
import {
forwardRef,
useRef,
useImperativeHandle
} from 'react';
const MyInput = forwardRef((props, ref) => {
const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({
// Only expose focus and nothing else
focus() {
realInputRef.current.focus();
},
}));
return <input {...props} ref={realInputRef} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
FlushSync
const newTodo = { id: nextId++, text: text };
setText('');
setTodos([ ...todos, newTodo]);
listRef.current.lastChild.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
上面代码中,我们给列表中添加一个新的todo项,setTodos之后,想要直接跳转到列表的最后一项,但是根据我们前面掌握的react渲染机制,这显然是不行的,每一次操作触发的渲染,都是一个批处理队列,setTodos所做的改变并不能直接生效。
import { useState, useRef } from 'react';
import { flushSync } from 'react-dom';
export default function TodoList() {
const listRef = useRef(null);
const [text, setText] = useState('');
const [todos, setTodos] = useState(
initialTodos
);
function handleAdd() {
const newTodo = { id: nextId++, text: text };
flushSync(() => {
setText('');
setTodos([ ...todos, newTodo]);
});
listRef.current.lastChild.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}
return (
<>
<button onClick={handleAdd}>
Add
</button>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<ul ref={listRef}>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</>
);
}
let nextId = 0;
let initialTodos = [];
for (let i = 0; i < 20; i++) {
initialTodos.push({
id: nextId++,
text: 'Todo #' + (i + 1)
});
}
可以使用flushSync这个方法,来实现dom的事实更新。