🌞Moon Will Know
🎖️

React 文档整理

Main

组件

React.Component

React 类组件的基类。类组件是 React.Component 的派生。

生命周期

notion image

挂载

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

更新

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

卸载

当组件从 DOM 中移除时会调用如下方法:
  • componentWillUnmount()

错误处理

当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
  • static getDerivedStateFromError()
  • componentDidCatch()

render()

  1. render() 方法是 class 组件中唯一必须实现的方法。
  1. 当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
      • React 元素。通常通过 JSX 创建。例如,<div /> 会被 React 渲染为 DOM 节点,<MyComponent /> 会被 React 渲染为自定义组件,无论是 <div /> 还是 <MyComponent /> 均为 React 元素。
      • 数组或 fragments。 使得 render 方法可以返回多个元素。
      • Portals。可以渲染子节点到不同的 DOM 子树中。
      • 字符串或数值类型。它们在 DOM 中会被渲染为文本节点。
      • 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && <Child /> 的模式,其中 test 为布尔类型。)
  1. render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。

constructor(props)

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。
  1. 如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
  1. 通常,在 React 中,构造函数仅用于以下两种情况:
      • 通过给 this.state 赋值对象来初始化内部 state。
  1. 在 constructor() 函数中不要调用 setState() 方法。如果你的组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state

componentDidMount()

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
  1. 这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅
  1. 你可以在 componentDidMount() 里直接调用 setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor() 中初始化 state。如果你的渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理

componentDidUpdate(preProps,preState,sanpshot)

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。
  1. 当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。
  1. 你也可以在 componentDidUpdate() 中直接调用 setState(),但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。
  1. 如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。

componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。 componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

shouldComponentUpdate(nextProps,nextState)

根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。 当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。
  1. 此方法仅作为性能优化的方式而存在。如果需要改写该生命周期时,应该考虑 React.PureComponet
  1. shouldComponentUpdate() 返回 false,则不会调用 UNSAFE_componentWillUpdate()render() 和 componentDidUpdate()

static getDerivedStateFromProps()

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。 此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props。例如,实现 <Transition> 组件可能很方便,该组件会比较当前组件与下一组件,以决定针对哪些组件进行转场动画。

getSnapshotBeforeUpdate(prevProps,preState)

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()。 此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。

static getDerivedStateFromError(error)

此生命周期会在后代组件抛出错误后被调用。 它将抛出的错误作为参数,并返回一个值以更新 state。

componentDidCatch(error,info)

此生命周期在后代组件抛出错误后被调用。 它接收两个参数:
  1. error —— 抛出的错误。
  1. info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息componentDidCatch() 会在“提交”阶段被调用,因此允许执行副作用。 它应该用于记录错误之类的情况。

实例属性和方法

state

组件中的 state 包含了随时可能发生变化的数据。state 由用户自定义,它是一个普通 JavaScript 对象。如果某些值未用于渲染或数据流(例如,计时器 ID),则不必将其设置为 state。此类值可以在组件实例上定义。永远不要直接改变 this.state,因为后续调用的 setState() 可能会替换掉你的改变。请把 this.state 看作是不可变的。

props

this.props 包括被该组件调用者定义的 props。需特别注意,this.props.children 是一个特殊的 prop,通常由 JSX 表达式中的子组件组成,而非组件本身定义。

setState(updater[,callback])

  1. setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式。
  1. setState() 并不总是立即更新组件。它会批量推迟更新。在 setState() 后可能无法获取到更新后的值,如果需要在值更新后立即使用,可以使用 componentDidUpdate 生命周期或 setState 的回调函数中使用。如果要强制 DOM 更新同步,可以使用 flushSync 包装。
  1. 除非 shouldComponentUpdate() 返回 false,否则 setState() 将始终执行重新渲染操作。
  1. updater 可以是一个对象,也可以是一个函数 (state,props)=>newState ,对象或函数的返回值将会被浅合并(类似于 Object.assign())到 state 中。
  1. updater 函数中接收的 state 和 props 都保证为最新。
  1. 如果在同一周期中对多个 setState 进行批处理,后续的 setState 将会覆盖之前的调用,值只会有一次改变。如果后续状态取决于当前状态,建议使用 updater 函数的形式代替。
this.setState({ name: 'Simon' }) this.setState((state,props)=>{ offset: state.data.length + props.pagesize },() => { console.log(this,state.offset) })

forceUpdate(callback)

  1. 默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。
  1. 调用 forceUpdate() 将致使组件调用 render() 方法,此操作会跳过该组件的 shouldComponentUpdate()。但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate() 方法。如果标记发生变化,React 仍将只更新 DOM。
  1. 通常你应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state

派生类属性

defaultProps

defaultProps 可以为 Class 组件添加默认 props。这一般用于 props 未赋值,但又不能为 null 的情况。

displayName

displayName 字符串多用于调试消息。通常,你不需要设置它,因为它可以根据函数组件或 class 组件的名称推断出来。如果调试时需要显示不同的名称或创建高阶组件,请参阅使用 displayname 轻松进行调试了解更多。

contextType

挂载在 class 上的 contextType 属性可以赋值为由 React.createContext() 创建的 Context 对象。此属性可以让你使用 this.context 来获取最近 Context 上的值。你可以在任何生命周期中访问到它,包括 render 函数中。
class MyClass extends React.Component { componentDidMount() { let value = this.context; /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */ } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context; /* 基于 MyContext 组件的值进行渲染 */ } } // 添加组件属性 MyClass.contextType = MyContext;

使用

class MyComponent extends React.Component { timer = null; // render 之前调用 constructor(props) { super(props); this.state = { name: 'Simon' } } // render之后 组件挂载之后,可以获取 DOM componentDidMount() { const main = document.querySelector('#main'); this.timer = setInterval(()=>{},10000) } // props 或 sate 变化时,render 之前触发 返回一个布尔值决定组件是否重新渲染 shouldComponentUpdate(nextProps,nextState) { } // 组件更新后调用,首次渲染时不会更新 componentDidUpdate(preProps,preState) { } // 处理组件抛出错误,埋点或显示错误UI componentDidCatch(error,info) { } // 组件卸载前 清除监听,定时器 componentWillUnmount() { clearInterval(this.timer) } render() { reutrn <div id="main">hello world</div> } }

React.pureComponent

React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了 shouldComponentUpdate(),如果 props 和 state 相同,render() 返回的结果相同。

方法

React.memo(component[,areEqual])

React.memo() 是一个高阶组件,如果组件在相同的 props 下渲染的结果相同,那么使用 React.memo() 包裹后的组件将记忆组件的渲染结果,在 props 浅比较相同的情况下跳过渲染组件并直接复用最近一次渲染的结果。
  1. 如果函数组件内部含有 useStateuseReduceruseContext 等 Hook 时,仍会重新渲染。
  1. 默认情况下 React.memo() 仅对 props 进行浅比较,如果需要进行深层比较或自行控制,需要传入一个自定义的 areEqual() 函数来实现。

createElement(type[,props,...children])

创建并返回指定类型的新 React 元素。其中的类型参数既可以是标签名字符串(如 'div' 或 'span'),也可以是 React 组件 类型 (class 组件或函数组件),或是 React fragment 类型。 使用 JSX 编写的代码将会被转换成使用 React.createElement() 的形式。如果使用了 JSX 方式,那么一般来说就不需要直接调用 React.createElement()

cloneElement(element[,config,...children])

以 element 元素为样板克隆并返回新的 React 元素。config 中应包含新的 props,key 或 ref。返回元素的 props 是将新的 props 与原始元素的 props 浅层合并后的结果。新的子元素将取代现有的子元素,如果在 config 中未出现 key 或 ref,那么原始元素的 key 和 ref 将被保留。 React.cloneElement() 几乎等同于(但 cloneElement() 没有克隆 ref 属性):
<element.type {...element.props} {...props}>{children}</element.type>

isValidElement(object)

验证对象是否为 React 元素,返回值为 true 或 false

React.Children

React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。

React.Children.map(children,fuction([thisArg]))

在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg。如果 children 是一个数组,它将被遍历并为数组中的每个子节点调用该函数。如果子节点为 null 或是 undefined,则此方法将返回 null 或是 undefined,而不会返回数组。

React.Children.forEach(children,fuction([thisArg]))

与[[React API#React.Children.map(children,fuction([thisArg]))]] 类似,但它不会返回一个数组。

React.Children.count(children)

返回 children 中的组件总数量,等同于通过 map 或 forEach 调用回调函数的次数。

React.Children.only(children)

验证 children 是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。

React.Children.toArray(children)

将 children 这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 key。当你想要在渲染函数中操作子节点的集合时,它会非常实用,特别是当你想要在向下传递 this.props.children 之前对内容重新排序或获取子集时。

React.Fragment

React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让 render() 方法中返回多个元素。或者使用简写<></>

React.createRef()

React.createRef 创建一个能够通过 ref 属性附加到 React 元素的 ref

React.forwardRef((props,ref)=>ReactNode)

React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。在以下两种场景中特别有用:

React.lazy()

React.lazy() 允许你定义一个动态加载的组件。这有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件。 一般同 import() 函数和 React.Suspense 组件一同使用。

<React.Suspense fallback={<Loading/>}>

React.Suspense 可以指定加载指示器,以防其组件树中的某些子组件尚未具备渲染条件。 如今,懒加载组件是 <React.Suspense> 支持的唯一用例。

React.CreateContext()

React.createContext(default)

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。

Context.Provider

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。 Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。从 Provider 到其内部 consumer 组件(包括 .contextType 和 useContext)的传播不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件跳过更新的情况下也能更新。

class.contextType

仅适用于消费单个 context 的情况,如果需要消费多个 context,则需要使用 Context.Consumer 嵌套。

Context.Consumer

<MyContext.Consumer> {value => /* 基于 context 值进行渲染*/} </MyContext.Consumer>
一个 React 组件可以订阅 context 的变更,此组件可以让你在函数式组件中可以订阅 context。 这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。传递给函数的 value 值等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue

ReactDOM

1. createPortal()

children 组件渲染到目标 container 上, children 在 React 中继续保持和嵌套组件的父子关系。一般用在弹窗等需要跳出 CSS 层级的场景。
createPortal(children,container[,key]);
使用方法:
const Component = () => { //... return ( //... { ReactDOM.createPortal(<div>我是弹窗</div>,document.body) } //... ) }

2. flushSync()

💫
flushSync会对性能产生很大影响。尽量少用。
强制 React 同步刷新提供的回调函数中的任何更新。这确保了 DOM 会被立即 更新。 在 React18 中,状态会批量更新。如果需要立即更新,那么可以使用flushSync()
使用方法:
flushSync(() => { setCount(count + 1); }); // 在这里 DOM 中的值已经更新

3. render()

💫
已经过时的方法,请使用 createRoot()
在提供的 container 里渲染一个 React 元素,并返回对该组件的引用或者针对无状态组件返回 null)。 如果 React 元素之前已经在 container 里渲染过,这将会对其执行更新操作,并仅会在必要时改变 DOM 以映射最新的 React 元素。 如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。
render(element, container[, callback])

4.createRoot()

createRoot(container[, options]);
根据给定的 container 返回一个根节点,具有可以渲染 React 元素到 DOM 中的 render 方法。和可以从 DOM 中卸载的 unmount 方法。 options 包含两个选项:
  • onRecoverableError :当React自动从错误中恢复时调用的可选回调。
  • identifierPrefixuseId() 钩子的前缀,如果同时存在多个 root 的时候可以避免冲突。
使用方法:
import * as ReactDOM from 'react-dom/client'; import App from './APP.tsx'; const root = ReactDOM.createRoot(document.querySelector('#root)); root.render(<App/>)
卸载:
root.unmount();

Hook

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

useState([initialState])

const [state, setState] = useState(initialState);
使用 initialState 初始化一个 state,返回一个 state,以及更新 state 的函数。
  1. 如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。该函数将接收先前的 state,并返回一个更新后的值。
  1. initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用。
  1. 如果你更新 State Hook 后的 state 与当前的 state 相同时,React 将跳过子组件的渲染并且不会触发 effect 的执行。React 可能仍需要在跳过渲染前渲染该组件,不过由于 React 不会对组件树的“深层”节点进行不必要的渲染,所以大可不必担心。如果你在渲染期间执行了高开销的计算,则可以使用 useMemo 来进行优化。
  1. 多个 setState 函数的更新会被合并为一次重新渲染。如果需要立即同步更新,则可以使用 flushSync(callback)callback 中调用。

useEffect(didUpdate[,dependences])

该 Hook 接收一个包含命令式、且可能有副作用代码的函数。 在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。
  1. 使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。
  1. 默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。

清除副作用

当有定时器或订阅等操作需要在组件卸载时清除,可以通过 useEffect 函数返回一个清除函数。 为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除
useEffect(()=>{ const timer = setInterval(()=>{],1000}) return () => { clearInterval(timer) } },[])

effect 的执行时机

  1. 与 componentDidMountcomponentDidUpdate 不同的是,传给 useEffect 的函数会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。
  1. 如果需要在浏览器完成布局与绘制之前执行,可以使用 useLayoutEffect Hook 来处理。
  1. 而且当 stateflushSync 包装后更新时, 传递给 useEffect 的函数将在屏幕布局和绘制之前同步执行。
  1. 即使在 useEffect 被推迟到浏览器绘制之后的情况下,它也能保证在任何新的渲染前启动。React 在开始新的更新前,总会先刷新之前的渲染的 effect。

effect 的条件执行

默认情况下,effect 会在每轮组件渲染完成后执行。这样会使 effect 在某些不必要的场景下也会执行。以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组,React 会对数组中的值进行浅比较,当数组中的值发生改变时踩会重新执行。

useContext(context)

const value = useContext(MyContext);
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。 当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。

useReducer(reducer,initialArg,init)

const [state, dispatch] = useReducer(reducer, initialState, init);
useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

惰性初始化

将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。这么做可以将用于计算 state 的逻辑提取到 reducer 外部,这也为将来对重置 state 的 action 做处理提供了便利。

跳过 dispatch

如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行。

useCallback(callback[,dependences])

const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
返回一个 memoized 回调函数。 只有当回调函数的某个依赖改变时才更新,当把回调函数作为 props 传递给优化后的组件时十分有用。 useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

useMemo(callback[,dependences])

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
返回一个 memoized 值。 只有当依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
  1. 缓存一个高开销的计算结果。
  1. 缓存一个字节点。

useRef(initialValue)

const refContainer = useRef(initialValue);
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。
  1. 最常见的用途是命令式地访问子组件。
  1. useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。
  1. 当 ref 对象内容发生变化时,useRef 并_不会_通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。

useImperativeHandle(ref,createHandle[,deps])

useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用:
function FancyInput(props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput);

useLayoutEffect

其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。 它会阻塞浏览器更新。

useDebugValue(value)

useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。

useDeferredValue(value)

useDeferredValue 接受一个值,并返回该值的新副本,该副本将推迟到更紧急地更新之后。如果当前渲染是一个紧急更新的结果,比如用户输入,React 将返回之前的值,然后在紧急渲染完成后渲染新的值。

useTransition

const [isPending, startTransition] = useTransition(); startTransition(() => { setCount(count + 1); })
返回一个状态值表示过渡任务的等待状态,以及一个启动该过渡任务的函数。startTransition 允许你通过标记更新将提供的回调函数作为一个过渡任务,isPending 指示过渡任务何时活跃以显示一个等待状态。

useId

const id = useId();
useId 是一个用于生成横跨服务端和客户端的稳定的唯一 ID 的同时避免 hydration 不匹配的 hook。

React 理念

纯函数

  1. 当输入相同时,返回值永远相同
  1. 纯函数没有副作用

高阶组件 HOC

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。 具体而言,高阶组件是参数为组件,返回值为新组件的函数。 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

render props

术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。 render props 将 行为的抽象抽离出来,通过使用一个函数将处理好的值传递给 组件来进行共享。 具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。
// 定义 const myRenderComponent = ({children}) => { const [state,setState] = useState(); // 行为的抽象 return (<div> {children(state)} </div>) } // 使用 const renderCustomer = () => { return (<div> <myRenderComponent> {state = > <AnotherComponent data={state}/> } </myRenderComponent> </div>) }

优化和自定义 Hook

usePrevious

通过这个自定义钩子来记住上一个 state 的值。
type usePrevious = <T>(state:T)=> T | undefined; const usePrevious:usePrevious = (state) => { const ref = useRef(); useEffect(()=>{ ref.current(state) },[state]) return state.current }
usePrevious 初次执行时,ref.current 的值为 undefined 。并设置了一个 effect :当 state 改变时更新 ref.current 的值。 当 state 值改变时,effect 触发,ref.current 的值改变。同时应用该钩子的组件重新渲染。ref.current 不重复初始化仍为上一次的值。同时设置 effect 等待下一次 state 的更新。

useStateCallback

type useStateCallback = <T>(initalValue?: T) => [T,(...args:[T,(state:T) => unknown]) => never]; const useStateCallback:useStateCallback = (initalValue) => { const [state,setState] = useState(initalValue); const callback = useRef(); useEffect(()=>{ callback.current?.(state) },[state]) return [state,(state,fn)=>{ setState(state); callback.current = fn }] }

拿到更新后的 state

延迟到更新后执行
  1. 将和更新后的值相关的执行体抽离到 useEffect(callback,[state]) 中。
  1. 将和更新后的值相关的执行体抽离到 compontDidUpdate() 生命周期中,并比对 state 进行判断。
  1. 使用 useRef 缓存 state,并在 useEffect 中更新 state
  1. 拓展 1,使用自定义钩子,useStateCallback()

避免不必要的组件更新

  1. 使用 shouldComponentUpdate ,在这个声明周期中比对 propsstate 并返回一个布尔值,如果返回 false 就会跳过 componentDidUpdaterender
  1. 使用 Reac.PureComponent ,它实现了 shouldComponentUpdate 并在 propsstate 未改变时跳过更新。
  1. 使用 React.memo 包裹组件,它会比对 props ,当 props 相同时,就会跳过更新。
  1. 使用 React.useMemo 缓存子组件,如果决定组件是否更新的依赖项数组没有改变,那么React.useMemo 会复用上一次返回的值。

惰性初始化高开销对象

使用 useRef 和 初始化方法来改造
function Image(props) { const ref = useRef(null); // ✅ IntersectionObserver 只会被惰性创建一次 function getObserver() { if (ref.current === null) { ref.current = new IntersectionObserver(onIntersect); } return ref.current; } // 当你需要时,调用 getObserver() // ... }

useCallback中读取经常变化的值

如果想要记住的函数是一个在渲染期间没有被用到的事件处理器。可以使用 Ref,并手动将最后提交的值保存在 Ref 中。
function Form() { const [text, updateText] = useState(''); const textRef = useRef(); useEffect(() => { textRef.current = text; // 把它写入 ref }); const handleSubmit = useCallback(() => { const currentText = textRef.current; // 从 ref 读取它 alert(currentText); }, [textRef]); // 不要像 [text] 那样重新创建 handleSubmit return ( <> <input value={text} onChange={e => updateText(e.target.value)} /> <ExpensiveTree onSubmit={handleSubmit} /> </> ); }
或抽离为 Hook:
function useEventCallback(fn, dependencies) { const ref = useRef(() => { throw new Error('Cannot call an event handler while rendering.'); }); useEffect(() => { ref.current = fn; }, [fn, ...dependencies]); return useCallback(() => { const fn = ref.current; return fn(); }, [ref]); }

根据 state 的变化派生新的值

  1. 使用 getDerivedStateFromProps 生命周期,在 render 前会执行并根据返回值来更新state。(不推荐)
  1. 使用 pureComponent,并将需要派生的值放在 render 函数的函数体中。
  1. 使用 useMemo 缓存高开销的值,或直接在函数式组件的函数体中运算。