一、React Hooks的闭包陷阱是如何产生的?如何避免?
- 产生原因:Hook依赖的state在闭包中被锁定,当异步操作使用旧闭包值时会出现数据不一致
scss
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
// 始终输出初始值0(闭包陷阱)
console.log(count);
}, 3000);
}, []); // ❌ 空依赖数组
}
- 解决方案:
- 使用函数式更新:
setCount(c => c + 1)
- 通过
useRef
保持最新值引用 - 正确声明依赖数组
二、如何实现自定义Hook的TypeScript类型推断?
使用泛型与返回元组类型:
scss
function useToggle<T extends boolean>(initialState: T): [T, () => void] {
const [state, setState] = useState(initialState);
const toggle = () => setState(!state);
return [state, toggle];
}
// 使用时可自动推断类型
const [isOpen, toggleOpen] = useToggle(false);
三、React.memo与useMemo的核心区别是什么?
React.memo | useMemo | |
---|---|---|
作用对象 | 组件级缓存 | 值/计算结果的缓存 |
触发条件 | props变化时重新渲染 | 依赖数组变化时重新计算 |
典型场景 | 避免子组件不必要的渲染 | 昂贵计算/复杂对象创建的优化 |
四、如何用React Context实现细粒度性能优化?
- 拆分Context:将高频更新与低频更新的数据分离
- 使用Selector模式:
ini
const UserContext = createContext();
const useUser = (selector) => {
const context = useContext(UserContext);
return selector(context);
}
// 组件中按需消费
const username = useUser(ctx => ctx.username);
- 配合
useMemo
防止不必要渲染
五、解释React并发模式(Concurrent Mode)的三个核心API
- startTransition:标记非紧急状态更新
javascript
jsxCopy Code
import { startTransition } from 'react';
// 用户输入立即响应,搜索结果延迟更新
startTransition(() => setSearchQuery(input));
- useDeferredValue:获取延迟版本的值
- Suspense:配合lazy加载组件时展示fallback
六、React服务端渲染(SSR)的hydration过程可能遇到什么问题?如何解决?
问题:
- 客户端与服务端初始渲染不一致导致hydration失败
- 第三方库依赖window对象引发SSR报错
解决方案:
- 使用
useEffect
/componentDidMount
隔离浏览器API调用 - 服务端渲染时通过
ReactDOMServer.renderToString
生成静态HTML - 客户端使用
ReactDOM.hydrateRoot
进行注水 - 检查
data-reactroot
属性是否一致
七、设计高阶组件时如何处理ref透传问题?
使用forwardRef
与Ref转发:
javascript
const withLogger = (WrappedComponent) => {
return React.forwardRef((props, ref) => {
useEffect(() => {
console.log('Component mounted');
}, []);
return <WrappedComponent {...props} ref={ref} />;
});
}
八、React错误边界的实现原理与限制
原理 :
通过类组件的static getDerivedStateFromError()
和componentDidCatch()
捕获子组件树错误
限制:
-
无法捕获以下错误:
- 事件处理器中的错误
- 异步代码(setTimeout、请求回调)
- 服务端渲染错误
- 错误边界组件自身的错误
九、如何用React实现双向数据绑定?
方案 :
组合使用value
与onChange
,支持自定义表单控件:
javascript
function useModel(initialValue) {
const [value, setValue] = useState(initialValue);
return {
value,
onChange: e => setValue(e.target.value),
// 支持非DOM场景
setValue
};
}
// 使用
const inputProps = useModel('');
return <input {...inputProps} />;
十、React性能优化中如何正确使用useCallback?
黄金法则:
-
必要性检查:仅在以下场景使用:
- 作为其他Hook的依赖项
- 作为memoized组件的prop
-
稳定依赖:确保依赖数组完整且变化合理
-
反模式:避免在渲染层直接创建函数
scss
// ✅ 正确用法
const handleClick = useCallback(() => {
// 依赖state时需要声明依赖数组
}, [dep1, dep2]);
// ❌ 不必要的useCallback
const unstableFunc = useCallback(() => {}, []);