小而美的系列教程,周末定时更新,后续会推出基于 React 从 0~1 开发的三方插件,如果有收获,记得给个点赞。
有过 React 开发经验的同学应该都用过 useRef ,你一般在什么场景使用?
说几个比较常用的,欢迎补充。
- 获取元素
- 获取子组件元素
- 防止闭包问题
获取元素
这应该是比较常见的了,例如 input 自动聚焦,当页面加载完成时,input 元素可自动聚焦。
const App = () => {
const ref = useRef<HTMLInputElement>(null);
// returns: { current: null }
/** 获取元素 */
useEffect(() => {
ref.current?.focus();
}, []);
return (
<>
<input type="text" ref={ref} />
</>
);
};
export default App;
当然 ref 也支持回调
const callback = (ele: HTMLInputElement) => ele.focus();
<input type="text" ref={callback} />
获取子组件元素
和前者比较类似,由于 ref 在 React 里面属于 key code,默认情况下是不能直接作为 props 传递的,需要借助 forwardRef。
const Counter = forwardRef((props, ref: React.ForwardedRef<any>) => {
return <input type="text" ref={ref} />;
});
/** 获取子组件元素 */
const App = () => {
const ref = useRef<HTMLInputElement>(null);
const handleOnClick = () => ref.current?.focus();
return (
<>
<Counter ref={ref} />
<button onClick={handleOnClick}>focus</button>
</>
);
};
export default App;
这样就可以获取到对应的 input 元素;我们可以更进一步,如果想获取子组件对应的方法或属性呢?
在不用 props callback 的情况下,可以借助 useImperativeHandle 实现:
const Counter = forwardRef((props, ref: React.ForwardedRef<any>) => {
const componentName = "Counter";
useImperativeHandle(ref, () => ({
componentName,
xx: () => 'xx',
}));
return <div ref={ref}>children</div>;
});
上层通过 ref.current
即可获取到对应返回值
防止闭包问题
我们在使用三方组件时,由于不清楚内部实现,有时会出现意想不到的结果。 例如下面这个示例,不论我们点击多少次 change data
, 当我们点击 log
时, 打印的结果永远是 []
const SomeComponent = ({ onReady }: { onReady: () => void }) => {
const onClick = useCallback(onReady, []);
return <button onClick={() => onClick()}>log</button>;
};
const App = () => {
const [data, setData] = useState<number[]>([]);
const onReady = () => {
console.log(data);
};
return (
<>
<p>{data.length}</p>
<SomeComponent onReady={onReady} />
<button onClick={()=> setData(data.concat([2]))}>change data</button>
</>
);
};
export default App;
对于这类问题,我们可以通过 ref 来获取最新数据,原因在于 useRef 返回的是 { current: null }
,将对应数据赋值给 current,在声明之后,引用地址是不变的。
const App = () => {
const [data, setData] = useState<number[]>([]);
const refData = useRef<number[]>(null);
refData.current = data;
const onReady = () => {
console.log(refData.current);
};
};
如果你意犹未尽:beta.reactjs.org/learn/manip…