react 新的hooks useDeferredValue 和 useTransition

257 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天,点击查看活动详情

useDeferredValue

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

首先有一个父组件,这个父组件不干什么,就是有一个state,然后改变这个state,这个state会传递给list这个子组件


export default function DeferredValue() {
  const [text, setText] = useState('骚帮')
  const handleChange = (e) => {
    setText(e.target.value)
  }
  return (
    <div className="App">
      <input value={text} onChange={handleChange} />
      <List text={text} />
    </div>
  )
}

子组件里是这个情况,自身有两个state,但是这两个state会根据props.text变动而变动,变动是都会变动的,但是有一个谁先变动谁变动说法,毕竟人类的进步的一大特征就是有文化的先上。这里也是分个优先级,并不是先来后到那么简单

特点如下,当大家都是普通的依赖时,就是传统的props.text,前面的effect总是先执行 但是当有使用useDeferredValue包裹的依赖时,依赖这个值的effect优先级就比较低,总是最后执行,当然如果都是依赖这种数据的effect,顺序也是从上到下。

const List = (props) => {
  const deferredText = useDeferredValue(props.text)
  const [list, setList] = useState([])
  const [list2, setList2] = useState([])
  useEffect(() => {
    console.log('deferredText')
    setList(
      new Array(10).fill(null).map(() => {
        return { name: deferredText, value: Math.random() }
      })
    )
  }, [deferredText])
  useEffect(() => {
    console.log('text')
    setList2(
      new Array(10).fill(null).map(() => {
        return { name: deferredText, value: Math.random() }
      })
    )
  }, [props.text])

  return (
    <>
      <ul>
        {list.map((item) => (
          <li key={item.value}>
            Hello:{item.name} value:{item.value}
          </li>
        ))}
      </ul>
      <ul>
        {list2.map((item) => (
          <li key={item.value}>
            Hello:{item.name} value:{item.value}
          </li>
        ))}
      </ul>
    </>
  )
}

image.png

 const deferredText = useDeferredValue(props.text)
  const [list, setList] = useState([])
  const [list2, setList2] = useState([])
  useEffect(() => {
    console.log('deferredText')
    setList(
      new Array(10).fill(null).map(() => {
        return { name: deferredText, value: Math.random() }
      })
    )
  }, [deferredText])
  useEffect(() => {
    console.log('text2')
    setList(
      new Array(10).fill(null).map(() => {
        return { name: props.text, value: Math.random() }
      })
    )
  }, [props.text])
  useEffect(() => {
    console.log('text')
    setList2(
      new Array(10).fill(null).map(() => {
        return { name: props.text, value: Math.random() }
      })
    )
  }, [props.text])

  useEffect(() => {
    console.log('deferredText2')
    setList2(
      new Array(10).fill(null).map(() => {
        return { name: deferredText, value: Math.random() }
      })
    )
  }, [deferredText])

上面effect的执行顺序也是这个道理,相同优先级的顺序从上到下,useDeferredValue的优先级比普通的数据的副作用执行时机优先级低。

image.png

useTransition

useTransiton和useDeferredValue这两个作用感觉一模一样

还是上面的例子,在父组件稍微改动一下,将setText这个过程的优先使用useTransition这个钩子控制一下。那么这个text就相当于被useDeferredValue了一下一样。依赖它更新的优先级就是最低级别的了,所以就算你再使用useDeferredValue包裹一下,他的优先级也不会再低了。

export default function DeferredValue() {
  const [text, setText] = useState('骚帮')
  const [isPedding, startTransition] = useTransition()
  const handleChange = (e) => {
    startTransition(() => setText(e.target.value))
  }
  return (
    <div className="App">
      <input value={text} onChange={handleChange} />
      <div>{text}</div>
      {isPedding ? "loading...." : <List text={text} />}
    </div>
  )
}

所以这时子组件的useEfect的更新顺序你猜的到吗? deferredText > text2 > text > deferredText2 就是按照副作用书写的顺序来执行了。

useDeferredValue 和 useTransition 区别

这两个作用几乎无区别,但是适合不同场景而已,当一个父组件的状态不一定是低优先级的时候,我们定然不可以直接使用useTransition控制更新过程,可以在某个期望这个更新优先级低的组件里使用useDeferredValue将这个props转成低优先级的。 当这个状态的优先级更新确定是低的时候,就可以使用useTransition包裹一下砸