React基础知识
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;
const handleSomeEvent = React.useCallback(() => {
// ...
}, [...])
<SomeChild onSomeEvent={handleSomeEvent}>
- 避免effect hooks频繁执行;
const doSomething = React.useCallback(() => {
// ...
}, [...])
// doSomething的逻辑无需复用且代码较少的话,也可以将doSomething的逻辑移入useEffect,这样的好处是将依赖都放在一起,便于维护;
React.useEffect(()=>{
// ...
doSomething()
// ...
},[doSomething])
- 上述基础上,如果新状态依赖于上一个状态计算而来,可以通过updater函数移除对state的依赖
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
// 创建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内部分为以下几类:
- 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
获取窗口尺寸
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;
}