首页
Preview

React 18中需要了解的 Hook

学习React 18需要了解的Hook

1. useDeferredValue

useDeferredValue是一个非常有用的hook,它可以用来推迟某个值的更新,以提高性能。它在处理用户输入或其他频繁变化的值时特别有用。通过将某个值包装在useDeferredValue中,React会推迟该值的更新,直到浏览器处于空闲状态。

import { useDeferredValue, useState } from 'react';

const SlowUI = () => (
  <>
    {Array(50000)
      .fill(1)
      .map((_, index) => (
        <span key={index}>{100000} </span>
      ))}
  </>
);

function App() {
  const [value, setValue] = useState(0);
  const deferredValue = useDeferredValue(value);

  const handleClick = () => {
    setValue(value + 1);
  };

  return (
    <>
      <button onClick={handleClick}>{value}</button>
      <div>DeferredValue: {deferredValue}</div>
      <div>
        <SlowUI />
      </div>
    </>
  );
}

export default App;

2. useTransition

useTransition()是一个用于过渡效果的hook。它返回过渡的状态和一个用于启动过渡的函数。

const [isPending, startTransition] = useTransition();

React状态更新可以分为两类:

紧急更新 - 反映直接的交互,例如键入、点击、按下、拖动等。 过渡更新 - 用于将UI从一个视图过渡到另一个视图。 在过渡中,更新会让位给更紧急的更新。下面是一个示例,将useTransition放置在src/App.js中:

import React, { useState, useTransition } from 'react';

function App() {
  const [isPending, startTransition] = useTransition();
  const [text, setText] = useState('');

  const handleChange = (event) => {
    startTransition(() => {
      setText(event.target.value);
    });
  };

  return (
    <div>
      <input type="text" value={text} onChange={handleChange} />
      {isPending ? <p>Loading...</p> : <p>Text: {text}</p>}
    </div>
  );
}

export default App;

在上述代码中,我们使用useTransition() hook来获取过渡的状态和startTransition()函数。我们还使用useState() hook来创建一个文本输入框的状态和更新函数。在handleChange函数中,我们使用startTransition()函数将文本更新操作包裹起来,以确保它在过渡期间能够让位给更紧急的更新。当isPending为true时,显示"Loading...",否则显示文本的内容。

3. useMutableSource

useMutableSource是一个高级的hook,它可以用来创建可变的数据源,以便在自定义的React Hooks中共享数据。它可以用来解决一些复杂的状态共享问题。

import { useMutableSource } from 'react';

function useCustomHook() {
  const mutableSource = useMutableSource(myMutableSource, getSnapshot, subscribe);

  // 使用mutableSource进行状态管理和更新

  return mutableSource;
}

4. useId

在Web应用程序中,有些情况下需要使用唯一的ID,例如:

<label for="ID">,其中for属性必须与相关元素的id属性相等,以将它们绑定在一起。 aria-labelledby,其中aria-labelledby属性可以接受多个ID。 useId()是一个生成唯一ID的hook:

这个ID在服务器和客户端之间是稳定的,避免了服务器端渲染时的混合问题。 这个ID在整个应用程序中是唯一的。对于多根应用程序,createRoot/hydrateRoot有一个可选的identifierPrefix属性,可以用于添加前缀以防止冲突。 这个ID可以附加前缀和/或后缀,以生成在组件中使用的多个唯一ID。这似乎是一个微不足道的功能。但是,useId是从useOpaqueIdentifier演变而来的,后者生成的是不可操作的不透明ID。

使用useId可以帮助我们生成唯一的ID,确保在应用程序中的各个组件中使用不重复的ID。这对于构建可靠的用户界面和确保无障碍性非常重要。

请注意,useId是一个自定义hook,并不是React的内置hook,你可以根据自己的需求实现它或使用现有的库,如下使用:

import { useId } from 'react';

const Comp1 = () => {
  const id = useId();
  return <div>Comp1 id({id})</div>;
};

const Comp2 = () => {
  const id = useId();
  return (
    <>
      <div>Comp2 id({id})</div>
      <label htmlFor={`${id}-1`}>Label 1</label>
      <div>
        <input id={`${id}-1`} type="text" />
      </div>
      <label htmlFor={`${id}-2`}>Label 2</label>
      <div>
        <input id={`${id}-2`} type="text" />
      </div>
    </>
  );
};

const Comp3 = () => {
  const id = useId();
  return (
    <>
      <div>Comp3 id({id})</div>
      <div aria-labelledby={`${id}-a ${id}-b ${id}-c`}>I am Comp3</div>
    </>
  );
};

function App() {
  return (
    <>
      <Comp1 />
      <Comp2 />
      <Comp3 />
    </>
  );
}

export default App;

5. useSyncExternalStore

useSyncExternalStore是一个推荐用于从外部数据源(存储)中读取和订阅数据的hook。

下面是该hook的函数签名:

const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot]);

该方法接受三个参数:

subscribe:一个函数,用于注册一个回调函数,该回调函数在存储更改时被调用。 getSnapshot:一个函数,返回存储的当前值。 getServerSnapshot:一个函数,返回在服务器渲染期间使用的快照。这是一个可选参数。 该方法返回存储的值,即state

如下代码:

import { useSyncExternalStore } from 'react';

function App() {
  const width = useSyncExternalStore(
    (listener) => {
      window.addEventListener('resize', listener);
      return () => {
        window.removeEventListener('resize', listener);
      };
    },
    () => window.innerWidth
    // () => -1,
  );

  return <p>Size: {width}</p>;
}

export default App;

在上述代码中,调用了useSyncExternalStore

subscribe:它为窗口调整大小事件监听器注册了一个回调函数。 getSnapshot:它返回当前浏览器窗口的宽度。 getServerSnapshot:这是用于服务器渲染的,但在此处不需要,可以简单地返回-1。

6. useInsertionEffect

useInsertionEffect是在React 18中引入的一个新的hook。它具有与useEffect相同的签名,但在所有DOM变化之前同步触发。也就是说,它在useLayoutEffect之前触发。它用于在读取布局之前向DOM插入样式。

useInsertionEffect主要用于CSS-in-JS库,例如styled-components。由于此hook的范围有限,它无法访问refs并且无法调度更新。

以下示例比较了useEffectuseLayoutEffectuseInsertionEffect

import { useEffect, useLayoutEffect, useInsertionEffect } from 'react';

function App() {
  useEffect(() => {
    console.log('useEffect');
    return () => {
      console.log('Cleanup useEffect');
    };
  }, []);

  useLayoutEffect(() => {
    console.log('useLayoutEffect');
    return () => {
      console.log('Cleanup useLayoutEffect');
    };
  }, []);

  useInsertionEffect(() => {
    console.log('useInsertionEffect');
    return () => {
      console.log('Cleanup useInsertionEffect');
    };
  }, []);

  return <div>Hello World</div>;
}

export default App;

在上述代码中,我们使用了三个不同的effect hook:useEffectuseLayoutEffectuseInsertionEffect。每个effect hook都输出一条消息,并在组件卸载时进行清理。

当我们运行这个示例时,我们可以观察到它们的触发顺序和清理顺序。useEffect在组件渲染后触发,useLayoutEffect在DOM变化之后触发,而useInsertionEffect在DOM变化之前触发。

请注意,useInsertionEffect是React 18中新增的一个hook,并且在特定的使用场景下才会使用。通常情况下,使用useEffectuseLayoutEffect就足够满足大多数需求。

7. useSyncExternalState

useSyncExternalState是一个用于同步外部状态的hook,在React 18中引入了对外部状态同步的原生支持。它可以用来将React组件的状态与外部的状态进行同步。

import { useSyncExternalState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  // 将外部状态与组件状态同步
  useSyncExternalState(count, setCount);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
}

在上述代码中,我们使用useSyncExternalState将组件的count状态与外部的状态进行同步。这样,当外部的状态发生变化时,组件的状态也会相应地更新。

8. useErrorHandler

useErrorHandler是一个用于处理错误的hook,在React 18中引入了对错误处理的原生支持。它可以用来捕获和处理组件中发生的错误。

import { useErrorHandler } from 'react';

function MyComponent() {
  const handleError = useErrorHandler();

  const fetchData = async () => {
    try {
      const response = await fetch('api/data');
      const data = await response.json();
      // 处理获取的数据
    } catch (error) {
      handleError(error);
    }
  };

  return (
    <div>
      <button onClick={fetchData}>Fetch Data</button>
    </div>
  );
}

在上述代码中,我们使用useErrorHandler来获取一个错误处理函数,并在fetchData函数中捕获和处理错误。这样,当发生错误时,我们可以通过错误处理函数来进行相应的处理。

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
Hedy
大家好!我是一位前端开发工程师,拥有6年以上的前端开发经验。我熟练掌握HTML、CSS、JavaScript等语言,能够灵活运用各种前端框架,如Vue、React、Uniapp、Flutter等。我注重理论与实践相结合,能够为学员提供丰富的案例和实践项目,并以生动、易懂的语言为学员讲解前端开发的核心知识和技能。我不仅注重传授技能,更关注学员的职业发展,希望通过我的教学,帮助学员成为一名优秀的前端开发工程师。

评论(0)

添加评论