Skip to content

使用并发模式

React 18引入并发模式,它允许你将标记更新作为一个transitions,这会告诉React它们可以被中断执行。这样可以把紧急的任务先更新,不紧急的任务后更新。

1. 使用startTransition

startTransition可以让你在不阻塞UI的情况下更新state:

jsx
import { startTransition, useState } from 'react'

function List({query}) {
  const items = []
  const word = "hello world"
  if(query!='' && word.includes(query)) {
    const splitWord = word.split(query)
    // 制造一个非常耗时的任务
    for (let i = 0; i < 20000; i++) {
      items.push(
        <li key={i}>{splitWord[0]}<span style={{color:'red'}}>{query}</span>{splitWord[1]}</li>
      )
    }
  }else{
    for (let i = 0; i < 20000; i++) {
      items.push(
        <li key={i}>{word}</li>
      )
    }
  }
  return (
    <ul>
      {items}
    </ul>
  )
}

function App() {
  const [msg, setMsg] = useState('')
  const [query, setQuery] = useState(msg)

  const handleChange = (e)=>{
    // 紧急任务
    setMsg(e.target.value)
    // 非紧急任务
    startTransition(()=>{
      setQuery(e.target.value)
    })
  }


  return (
    <div>
      <input type="text" onChange={handleChange} value={msg} />
      <List query={query}/>
    </div>
  )
}

export default App

运行效果:
Alt text 可以看到输入框不再受到影响,输入不再卡顿,因为输入框是一个受控组件,它需要jsx渲染完成才能在页面上显示最新结果,使用startTransition表示了不紧急的任务,提高了输入体验。

2. 使用useTransition

useTransition钩子函数提供更好的并发体验:

jsx
import { useState, useTransition } from 'react'

function List({query}) {
  const items = []
  const word = "hello world"
  if(query!='' && word.includes(query)) {
    const splitWord = word.split(query)
    // 制造一个非常耗时的任务
    for (let i = 0; i < 20000; i++) {
      items.push(
        <li key={i}>{splitWord[0]}<span style={{color:'red'}}>{query}</span>{splitWord[1]}</li>
      )
    }
  }else{
    for (let i = 0; i < 20000; i++) {
      items.push(
        <li key={i}>{word}</li>
      )
    }
  }
  return (
    <ul>
      {items}
    </ul>
  )
}

function App() {
  const [msg, setMsg] = useState('hello')
  const [query, setQuery] = useState('hello')
  const [pendding, startTransition] = useTransition()
  const handleChange = (e)=>{
    // 紧急任务
    setMsg(e.target.value)
    // 非紧急任务
    startTransition(()=>{
      setQuery(e.target.value)
    })
  }


  return (
    <div>
      <input type="text" onChange={handleChange} value={msg} />
      {pendding && "loading..."}
      <List query={query}/>
    </div>
  )
}
export default App

Alt text

3. 使用useDeferredValue

useDeferredValue只返回一个延迟对象:

jsx
import { useDeferredValue, useState } from 'react'

function List({query}) {
  const items = []
  const word = "hello world"
  if(query!='' && word.includes(query)) {
    const splitWord = word.split(query)
    // 制造一个非常耗时的任务
    for (let i = 0; i < 20000; i++) {
      items.push(
        <li key={i}>{splitWord[0]}<span style={{color:'red'}}>{query}</span>{splitWord[1]}</li>
      )
    }
  }else{
    for (let i = 0; i < 20000; i++) {
      items.push(
        <li key={i}>{word}</li>
      )
    }
  }
  return (
    <ul>
      {items}
    </ul>
  )
}

function App() {
  const [msg, setMsg] = useState('hello')
  // msg的延迟生效的副本对象, 哪里用query,哪里就是延迟生效
  const query= useDeferredValue(msg)
  const handleChange = (e)=>{
    // 紧急任务
    setMsg(e.target.value)
  }

  return (
    <div>
      <input type="text" onChange={handleChange} value={msg} />
      <List query={query}/>
    </div>
  )
}
export default App