React有哪些hooks
- useState
- useEffect
- useRef
- useMemo
- useCallback
- useContext
- useReducer
- useLayoutEffect
- useTransition
- useId
- useDebugValue
- useDeferredValue
useLayoutEffect和useEffect的区别
useEffect:异步执行,在浏览器完成渲染后(页面已绘制)
useLayoutEffect:同步执行,在 DOM 更新后、浏览器绘制前(用户不可见),适用于需要读取 DOM 布局或强制同步更新的场景(如测量 DOM、修改样式)。
如何借助interSectionObserver实现一个dom可见性的自定义hook
有一个关键点是Ref的更新在useEffect执行前
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import { useEffect, useState, useRef } from 'react'; const useInView = ( options = { root: null, rootMargin: '0px 0px', threshold: 1, }, triggerOnce = false, ) => { const [inView, setInView] = useState(false); const targetRef = useRef(null);
useEffect(() => { const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { setInView(true); if (triggerOnce) { observer.unobserve(entry.target); } } else { setInView(false); } }); }, options);
if (targetRef.current) { observer.observe(targetRef.current); }
return () => { if (targetRef.current) { observer.unobserve(targetRef.current); } }; }, [options, triggerOnce]);
return [targetRef, inView]; };
export default useInView;
|
useEffect 中如何使用 async/await
- async函数抽离到外部
1 2 3 4 5 6 7 8 9
| async function fetchMyAPI() { let response = await fetch("api/data"); response = await res.json(); ådataSet(response); }
useEffect(() => { fetchMyAPI(); }, []);
|
- async立即执行函数
1 2 3 4 5
| useEffect(() => { (async function anyNameFunction() { await loadContent(); })(); }, []);
|
- ahooks - useAsyncEffect
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
| function useAsyncEffect( effect: () => AsyncGenerator<void, void, void> | Promise<void>, deps?: DependencyList, ) { useEffect(() => { const e = effect(); let cancelled = false; async function execute() { if (isAsyncGenerator(e)) { while (true) { const result = await e.next(); if (result.done || cancelled) { break; } } } else { await e; } } execute(); return () => { cancelled = true; }; }, deps); }
|
React hooks如何实现类似Redux的状态管理
useContext+useReducer
React key的作用
虚拟dom的标识,diff用
React useCallback使用场景
- 作为props传递的函数,集合memo一起使用;
- 作为更新触发的依赖项 主要目的是为了避免高昂的计算和不必要的重复渲染
React闭包陷阱
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
| import React, { useState, useEffect } from 'react';
function Counter() { const [count, setCount] = useState(0);
useEffect(() => { const intervalId = setInterval(() => { console.log('Stale count in setInterval:', count); setCount(count + 1); }, 1000);
return () => clearInterval(intervalId); }, []); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
export default Counter;
|
React reconcile
https://zh-hans.legacy.reactjs.org/docs/reconciliation.html#motivation
当多次重复点击按钮时,以下三个 Heading 是如何渲染的
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 30 31 32 33 34 35 36 37
| import React, { memo, useMemo, useState } from "react"; const Heading = memo(({ style, title }) => { console.log("Rendered:", title); return <h1 style={style}>{title}</h1>; }); export default function App() { const [count, setCount] = useState(0); const normalStyle = { backgroundColor: "teal", color: "white", }; const memoizedStyle = useMemo(() => { return { backgroundColor: "red", color: "white", }; }, []); return ( <> <button onClick={() => { setCount(count + 1); }} > Increment {count} </button> <Heading style={memoizedStyle} title="Memoized" /> <Heading style={normalStyle} title="Normal" /> <Heading title="React.memo Normal" /> </> ); }
|
setState代码输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { useState } from "react"; export default function App() { const [todo, setTodo] = useState({ id: 1, status: "TODO" }); return ( <div className="App"> <button onClick={() => { todo.status = !todo.status; setTodo(todo); }} > Toggle Status </button> <h1>{todo.status}</h1> </div> ); }
|
useCallback和useMemo性能优化
优化React项目性能
- 避免不必要的渲染,shouldComponentUpdate、React.memo、React.useMemo、React.useCallback。
- 代码分割,React.lazy 动态加载组件
- 使用
react-query
,对请求响应进行缓存、重发等,避免多次请求,减少网络 IO 消耗及优化渲染次数
- 使用
useDebounce
,对值及事件处理函数进行防抖,避免状态频繁变动,优化渲染次数
- 使用
useImmer
React的新特性