392 - 《MDH Weekly 2023 回顾(1)- React Part 1》
年底了,做下 MDH Weekly 一年的文章回顾。我把今年周刊里的文章重新理了一遍,删选了觉得目前看仍旧有价值的文章,做了下分类并重新翻了一遍。以下是 React 的部分。
1、ref 属性除了和 useRef 结合使用外,容易忽略的是他还可以作为 callback 使用,比如 <div ref={(el) => {}} />
,其在 DOM 元素创建和删除时被调用,删除时传入 null。一些应用场景包括,1)DOM 元素挂载时滚动到该元素,比如 <li ref={isLast ? scrollTo : undefined} />
,2)测量 DOM 元素尺寸,比如 <div ref={measureRef} />
,3)Modal + createPortal 创建模态窗,<div ref={setModalElement} />
。详见 https://julesblom.com/writing/ref-callback-use-cases#4-share-the-dom-ref
2、关于受控和不受控。1)简单说,就是「是否受控」即「表单元素的值是否由 React 控制」,2)一个注意点,受控的初始值如果为空,要给空字符串而不要给 undefined,比如 const [val, setVal] = useState()
,val 为 undefined 会让表单元素变成不受控,3)当需要唯一 id 时,比如用于可访问性和可用性的 htmlFor,可以用 useId()
生成,好处是 SSR 友好、多实例友好、re-render 稳定等。详见 https://www.joshwcomeau.com/react/data-binding/
3、如何在 React 中实现 debounce,代码如下。如果你手写 debounce,可能会遇到几个坑,1)如果只有 delay 效果而没有 debounce 效果,那通常是因为 debounce() 每次 re-render 时都被调用了,而 debounce() 理论上只应该被调一次,所以用 useMemo() + 空依赖以避免重复执行,2)如果 callback 里取 React 的 state 或 props,始终是初始值,那说明 callback 没有在 re-render 时被刷新,那需要用 useRef + useEffect,当 callback 更新时刷新 ref。详见 https://www.developerway.com/posts/debouncing-in-react
const useDebounce = (callback) => {
const ref = useRef();
useEffect(() => {
ref.current = callback;
}, [callback]);
const debouncedCallback = useMemo(() => {
const func = () => {
ref.current?.();
};
return debounce(func, 1000);
}, []);
return debouncedCallback;
};
4、关于错误处理。直接 try…catch 会有不少限制,但使用 React 官方的 ErrorBoundary 也有不少限制。这个限制是,React生命周期之外的错误不捕获,比如 resolved promises, async code with setTimeout, various callbacks and event handlers。怎么解?dan 给了个 Hack 方案,简单说就是 try {} catch(e) { setState(() => throw e) }
。基于此,我们可以稍微封装下,比如 useThrowAsyncError
或 useCallbackWithErrorHandling
,这样就不需要每次声明一个新的 setState 了。同时也可以考虑用 bvaughn/react-error-boundary 。详见 https://www.developerway.com/posts/how-to-handle-errors-in