Skip to content

useState

useState 是一个 Hook,因此你只能在 组件的顶层自定义 Hook 中调用它。你不能在循环或条件语句中调用它。

在严格模式中,React 将 两次调用初始化函数,以 帮你找到意外的不纯性。这只是开发时的行为,不影响生产。

tsx
const [state, setState] = useState(initialState)
避免使用 (会改变原始数组)推荐使用 (会返回一个新数组)
添加元素 push,unshiftconcat,[...arr] 展开语法(例子)
删除元素 pop,shift,splicefilter,slice(例子)
替换元素 splice,arr[i] = ... 赋值map(例子)
排序 reverse,sort先将数组复制一份(例子)

useState可以接受一个函数,可以在函数里面编写逻辑,初始化值,注意这个只会执行一次,更新的时候就不会执行了。

在使用setObject的时候,可以使用Object.assign合并对象 或者 ... 合并对象,不能单独赋值,不然会覆盖原始对象。

更新机制

异步更新

tsx
let [index, setIndex] = useState(0)
const heandleClick = () => {
    setIndex(index + 1)
    console.log(index,'index') //0
  }

当我们多次以相同的操作更新状态时,React 会进行比较,如果值相同,则会屏蔽后续的更新行为。

tsx
  let [index, setIndex] = useState(0)
  const heandleClick = () => {
    setIndex(index + 1) //1
    setIndex(index + 1) //1
    setIndex(index + 1) //1
    console.log(index,'index')
  }

为了解决这个问题,你可以向setIndex 传递一个更新函数,而不是一个状态。

tsx
  let [index, setIndex] = useState(0)
  const heandleClick = () => {
    setIndex(index => index + 1) //1
    setIndex(index => index + 1) //2
    setIndex(index => index + 1) //3
  }

为啥嘞,因为传入的参数是真正的值

useReducer

React提供的一个高级Hook,没有它我们也可以正常开发,但是useReducer可以使我们的代码具有更好的可读性,可维护性(对的,没有什么特别的)。与useState不同的是, useReducer 是集中式的管理状态的。

tsx
const [state, dispatch] = useReducer(
  (state, action) => {
    return state;
  },
  {
    customStateValue: 1
    //...
  },
  initFunction?
)
//如果编写了init函数,则默认值使用init函数的返回值,否则使用initialArg。

useSyncExternalStore

React 18 引入的一个 Hook,用于从外部存储(例如状态管理库、浏览器 API 等)获取状态并在组件中同步显示。这对于需要跟踪外部状态的应用非常有用。

  • 订阅外部 store 例如(redux,Zustand)
  • 订阅浏览器API 例如(online,storage,location)等
  • 抽离逻辑,编写自定义hooks
  • 服务端渲染支持
tsx
const res = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
  • subscribe:用来订阅数据源的变化,接收一个回调函数,在数据源更新时调用该回调函数。
  • getSnapshot:获取当前数据源的快照(当前状态)。
  • getServerSnapshot?:在服务器端渲染时用来获取数据源的快照。

如果 getSnapshot 返回值不同于上一次,React 会重新渲染组件。如果总是返回一个不同的值,会进入到一个无限循环。

其实是一个比较简单的封装吧,你的第一个参数告诉了它怎么订阅和取消订阅,并且还接受一个何时执行同步的回调函数,你的第二个参数又是一个获取状态数据的函数,那么内部大致是这样的

tsx
function useSyncExternalStore(subs, getdata){
  [state, setState] = useState(getdata)
  subs(()=>{
    setState(getdata())
  })
  return state
}

useTransition

React 18 中引入的一个 Hook,用于管理 UI 中的过渡状态,特别是在处理长时间运行的状态更新时。它允许你将某些更新标记为“过渡”状态,这样 React 可以优先处理更重要的更新,比如用户输入,同时延迟处理过渡更新。

主要关注点是状态的过渡。它允许开发者控制某个更新的延迟更新,还提供了过渡标识,让开发者能够添加过渡反馈。是更新状态的任务是低优先级的。

startTransition内部代码必须是同步的,否则等于没用。

tsx
const [isPending, startTransition] = useTransition();

调用startTransition就会处于pending状态,传入的回调函数会以较低的优先级运行,运行完成后pending才会修改。

useTransition 的核心原理是将一部分状态更新处理为低优先级任务,这样可以将关键的高优先级任务先执行,而低优先级的过渡更新则会稍微延迟处理。这在渲染大量数据、进行复杂运算或处理长时间任务时特别有效。React 通过调度机制来管理优先级:

  • 高优先级更新:直接影响用户体验的任务,比如表单输入、按钮点击等。
  • 低优先级更新:相对不影响交互的过渡性任务,比如大量数据渲染、动画等,这些任务可以延迟执行。
jsx
import React, { useState, useTransition } from 'react';

function App() {
  // 使用useTransition获取状态和转换函数
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    // 将状态更新标记为非紧急更新
    startTransition(() => {
      setCount(c => c + 1);
    });
  };
  
  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      {/* 根据过渡状态显示不同内容 */}
      {isPending ? 'Loading...' : <p>Count: {count}</p>}
    </div>
  );
}

export default App;

useDeferredValue

用于延迟某些状态的更新,直到主渲染任务完成。这对于高频更新的内容(如输入框、滚动等)非常有用,可以让 UI 更加流畅,避免由于频繁更新而导致的性能问题。

主要关注点是单个值的延迟更新。它允许你把特定状态的更新标记为低优先级。

tsx
import React, { useState, useDeferredValue } from 'react';

function App() {
  // 声明文本状态变量,用于存储输入框的值
  const [text, setText] = useState('');
  
  // 使用 useDeferredValue 创建延迟更新的文本值
  const deferredText = useDeferredValue(text);
  
  // 处理输入框变化的函数
  const handleChange = (e) => {
    setText(e.target.value);
  };
  
  return (
    <div>
      {/* 输入框,双向绑定到 text 状态 */}
      <input type="text" value={text} onChange={handleChange} />
      
      {/* 显示延迟更新的文本值 */}
      <p>{deferredText}</p>
    </div>
  );
}

export default App;

当 useDeferredValue 接收到与之前不同的值(内部使用 Object.is 进行比较)时,除了当前渲染(此时它仍然使用旧值),它还会安排一个后台重新渲染。这个后台重新渲染是可以被中断的

(如果 value 有新的更新,React 会从头开始重新启动后台渲染。举个例子,如果用户在输入框中的输入速度比接收延迟值的图表重新渲染的速度快,那么图表只会在用户停止输入后重新渲染。)

tsx
const deferredValue = useDeferredValue(value)

React19添加了第二个参数initValue,在组件的初始渲染中返回它作为 value , 并在后台安排一个使用返回的 deferredValue 重新渲染。

tsx
function Search({deferredValue}) {
  // On initial render the value is ''.
  // Then a re-render is scheduled with the deferredValue.
  const value = useDeferredValue(deferredValue, '');
  
  return (
    <Results query={value} />
  );
}

可以理解为deferredValue的值晚于等于value的值,value可以是任意值

注意:

useDeferredValue 并不是防抖,防抖是需要一个固定的延迟时间,譬如1秒后再处理某些行为,但是useDeferredValue并不是一个固定的延迟,它会根据用户设备的情况进行延迟,当设备情况好,那么延迟几乎是无感知的