React常见知识点
基础API或特性:
- ClassComponent生命周期(含unsafe)
- Hooks如何模拟ClassComponent生命周期
- useEffect VS useLayoutEffect
- useReducer VS useState
- setState是同步还是异步执行
- HOC
- Props VS State
- className VS style
- ErrorBoundary
- 自定义Hook
- React Key
- React Context
心智模型:
- 受控组件 VS 非受控组件
- 展示组件 VS 容器组件
- Class Component VS Function Component
- useEffect VS componentDidMount, componentDidUpdate and componentWillUnmount
经典交互
- 虚拟列表
- 分页
- 滚动加载
- 抽屉(Panel)
JSX
JSX是React对JS的语法扩展,在打包后会被替换成createElement,在React App第一次Mount时,createElement构建ReactElement对象,所有ReactElement以props.children连接起来构成一个重重嵌套的ReactElement对象;
ReactElement也称React DOM,在实现Fiber之前,Diff算法是基于ReactElement进行的,是不可打断的;
Function Component VS Class Component
React推荐使用函数组件和Hooks编写新的应用程序,函数组件优势主要体现在提高开发效率、维护成本降低,主要原因有以下几点:
- 函数粒度更小:函数比Class更容易拆分,更容易复用,更好避免巨石组件的出现;
- 纯函数增强程序稳定性,降低debug难度:将函数式编程思想应用到组件开发中,函数式编程的基础就是纯函数,相同输入一定得到相同输出;
- Effect Hooks降低了开发者的心智负担:函数组件中主要通过useEffect处理副作用,偶尔用到useLayoutEffect,而Class组件则要考虑选用哪个生命周期方法,且部分生命周期方法在Concurrent模式中是非安全的,React无法保证这些unsafe生命周期方法一定会执行到;
- React会减少甚至在未来版本移除Class组件部分特性的支持:如部分unsafe的生命周期方法;
React16 Hook
- useState(state),useState(() => state)
- useReducer(reducer, initialState, init?),reducer接收上一个state和被dispatch的action,返回新state,init接收initialState,返回一个初始state,忽略后初始state默认为initialState;
- useMemo(value);
- useCallback(callback);
- useEffect()
- useLayoutEffect()
- useContext(context)
- useRef()
React18新增Hook
- useTransition(timeoutMs): [loading, start],标记一个过渡更新,紧急更新处理完再处理过渡更新,一旦超时则对其强制更新;
- useDeferredValue(value, {timeoutMs})`,标记一个延迟更新的变量,紧急更新处理完再更新变量的值,一旦超时则强制更新变量的值;
- useId(),返回一个唯一id;
- useInsertionEffect(callback,dependencies),在render结束,还不能访问DOM时执行;
- useExternalStore(subscribe:(setState)=>any,getExternalState),同步外部数据源;
注:
- 对于useTransition,可看作useState和startTransition;
- 对于useDeferredValue,可看作是useState、useEffect和startTransition;
useCallback最佳实践
综合来讲,useCallback用于缓存函数,指定若干依赖项,当依赖项变化时函数才会更新,其应用场景如下:
- 缓存传递给子组件的函数,避免子组件频繁render;
1
2
3
4
5const handleSomeEvent = React.useCallback(() => {
// ...
}, [...])
<SomeChild onSomeEvent={handleSomeEvent}> - 避免effect hooks频繁执行;
1
2
3
4
5
6
7
8
9
10const doSomething = React.useCallback(() => {
// ...
}, [...])
// doSomething的逻辑无需复用且代码较少的话,也可以将doSomething的逻辑移入useEffect,这样的好处是将依赖都放在一起,便于维护;
React.useEffect(()=>{
// ...
doSomething()
// ...
},[doSomething]) - 上述基础上,如果新状态依赖于上一个状态计算而来,可以通过updater函数移除对state的依赖
1
2
3
4
5
6
7
8
9const [n, setN] = React.useState(0);
// 常规写法:状态变化时更新函数
const handleSomeEvent = React.useCallback(() => {
setN(n + 1);
}, [n])
// 优化写法:通过updater函数去指定state的更新,移除对state的依赖;
const handleSomeEvent = React.useCallback(() => {
setN((prev) => prev + 1);
}, [])
React Context
1 | // 创建context对象 |
React组件类型
React内部分为以下几类:
- FunctionComponent = 0;
- ClassComponent = 1;
- IndeterminateComponent = 2; // Before we know whether it is function or class
- HostRoot = 3; // Root of a host tree. Could be nested inside another node.
- HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
- HostComponent = 5;
- HostText = 6;
- Fragment = 7;
- Mode = 8;
- ContextConsumer = 9;
- ContextProvider = 10;
- ForwardRef = 11;
- Profiler = 12;
- SuspenseComponent = 13;
- MemoComponent = 14;
- SimpleMemoComponent = 15;
- LazyComponent = 16;
- IncompleteClassComponent = 17;
- DehydratedFragment = 18;
- SuspenseListComponent = 19;
- ScopeComponent = 21;
- OffscreenComponent = 22;
- LegacyHiddenComponent = 23;
- CacheComponent = 24;
自定义Hook
获取窗口尺寸
1 | const useWindowSize = () => { |