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编写新的应用程序,函数组件优势主要体现在提高开发效率、维护成本降低,主要原因有以下几点:

  1. 函数粒度更小:函数比Class更容易拆分,更容易复用,更好避免巨石组件的出现;
  2. 纯函数增强程序稳定性,降低debug难度:将函数式编程思想应用到组件开发中,函数式编程的基础就是纯函数,相同输入一定得到相同输出;
  3. Effect Hooks降低了开发者的心智负担:函数组件中主要通过useEffect处理副作用,偶尔用到useLayoutEffect,而Class组件则要考虑选用哪个生命周期方法,且部分生命周期方法在Concurrent模式中是非安全的,React无法保证这些unsafe生命周期方法一定会执行到;
  4. 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用于缓存函数,指定若干依赖项,当依赖项变化时函数才会更新,其应用场景如下:

  1. 缓存传递给子组件的函数,避免子组件频繁render;
    1
    2
    3
    4
    5
    const handleSomeEvent = React.useCallback(() => {
    // ...
    }, [...])

    <SomeChild onSomeEvent={handleSomeEvent}>
  2. 避免effect hooks频繁执行;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const doSomething = React.useCallback(() => {
    // ...
    }, [...])

    // doSomething的逻辑无需复用且代码较少的话,也可以将doSomething的逻辑移入useEffect,这样的好处是将依赖都放在一起,便于维护;
    React.useEffect(()=>{
    // ...
    doSomething()
    // ...
    },[doSomething])
  3. 上述基础上,如果新状态依赖于上一个状态计算而来,可以通过updater函数移除对state的依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const [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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 创建context对象
const ThemeContext = React.createContext(null);

// hooks获取context value
const Article = () => {
const theme = React.useContext(ThemeContext);

return <div>{theme}</div>;
}
// Consumer组件获取context value
const Navigator = () => {
return <ThemeContext.Consumer>{
(theme) => {
return <div>{theme}</div>;
}
}</ThemeContext.Consumer>;
}
// Provider提供context value
const App = () => {
const [theme, setTheme] = React.useState("light");

return <ThemeContext.Provider value={theme}>
<button onClick={()=>{
setTheme("dark");
}}>Dark Theme</button>
<Article />
<Navigator/>
</ThemeContext.Provider>
}

React组件类型

React内部分为以下几类:

  1. FunctionComponent = 0;
  2. ClassComponent = 1;
  3. IndeterminateComponent = 2; // Before we know whether it is function or class
  4. HostRoot = 3; // Root of a host tree. Could be nested inside another node.
  5. HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
  6. HostComponent = 5;
  7. HostText = 6;
  8. Fragment = 7;
  9. Mode = 8;
  10. ContextConsumer = 9;
  11. ContextProvider = 10;
  12. ForwardRef = 11;
  13. Profiler = 12;
  14. SuspenseComponent = 13;
  15. MemoComponent = 14;
  16. SimpleMemoComponent = 15;
  17. LazyComponent = 16;
  18. IncompleteClassComponent = 17;
  19. DehydratedFragment = 18;
  20. SuspenseListComponent = 19;
  21. ScopeComponent = 21;
  22. OffscreenComponent = 22;
  23. LegacyHiddenComponent = 23;
  24. CacheComponent = 24;

自定义Hook

获取窗口尺寸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const useWindowSize = () => {
const [size, setSize] = React.useState({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
});

const resizeHandler = () => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
})
}

React.useEffect(() => {
window.addEventListener("resize", resizeHandler);
return () => {
window.removeEventListener("resize", resizeHandler);
}
});


return size;
}