React 19 用到的新特性

最近在关注 React 19 的变化,比如 use、ref、useOptimistic 乐观更新等。

  • use:请求接口,请求等待中,显示 loading,请求成功,展示数据。接收一个 Promise,会阻塞 render 继续渲染,需要配套 <Suspense> 处理 loading,配套 <ErrorBoundary> 来处理异常。
  • ref 支持返回一个 cleanup 函数,在组件卸载时会调用该函数。
  • useOptimistic 乐观更新:收藏点赞的场景,用户触发后,立即更新 UI,如果请求失败后,再回滚 UI。

use

use 是 React 19 新增的一个特性,支持处理 Promise 和 Context。

假如我们要实现这样一个需求:请求接口数据,请求过程中,显示 loading,请求成功,展示数据。

以前我们可能会这样写代码

function ReactUseDemo() {
  const [data, setData] = useState("");
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    getList()
      .then((res) => {
        setData(res);
        setLoading(false);
      })
      .catch(() => {
        setLoading(false);
      });
  }, []);

  if (loading) return <div>Loading...</div>;

  return <div>{data}</div>;
}

通过 use 我们可以把代码改造成下面这样

export default function ReactUseDemo() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <ChildCompont />
    </Suspense>
  );
}

function ChildCompont() {
  const data = use(getData());
  return <div>{data}</div>;
}

use 接收一个 Promise,会阻塞 render 继续渲染,通常需要配套 Suspense 处理 loading 状态,需要配套 ErrorBoundary 来处理异常状态。

另外 use 也支持接收 Context,类似之前的 useContext,但比 useContext 更灵活,可以在条件语句和循环中使用。

function MyPage() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  );
}

function Form() {
  const theme = use(ThemeContext);
  ......
}

use 的使用有一些注意事项

  • 需要在组件或 Hook 内部使用
  • use 可以在条件语句(比如 if)或者循环(比如 for)里面调用

ref

在之前,父组件传递 ref 给子组件,子组件如果要消费,则必须通过 forwardRef 来消费。

function RefDemo() {
  const inputRef = useRef(null);
  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <Input ref={inputRef} />
      <button onClick={focusInput}>Focus</button>
    </div>
  );
}

const Input = forwardRef((props, ref) => {
  return <input ref={ref} />;
});

React 19 开始,不需要使用 forwardRef 了,ref 可以作为一个普通的 props 了。

export const Input = ({ ref }) => {
  return <input ref={ref} />;
};

ref 支持返回 cleanup 函数

ref 支持返回一个 cleanup 函数,在组件卸载时会调用该函数。

<input
  ref={(ref) => {
    // ref created

    // NEW: return a cleanup function to reset
    // the ref when element is removed from DOM.
    return () => {
      // ref cleanup
    };
  }}
/>

useOptimistic 乐观更新

乐观更新是一种常见的体验优化手段,在发送异步请求之前,我们默认请求是成功的,让用户立即看到成功后的状态。

先来看看官方提供的例子:提交表单更新 name,可以立即将新的 name 更新到 UI 中。请求成功则 UI 不变,请求失败则 UI 回滚。

function ChangeName() {
  const [name, setName] = useState("");

  // 定义乐观更新的状态
  const [optimisticName, setOptimisticName] = useOptimistic(name);

  const submitAction = async (formData) => {
    const newName = formData.get("name");
    // 请求之前,先把状态更新到 optimisticLike
    setOptimisticName(newName);
    try {
      await updateName(newName);
      // 成功之后,更新最终状态
      setName(newName);
    } catch (e) {
      console.error(e);
    }
  };

  return (
    <form action={submitAction}>
      <p>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input type="text" name="name" disabled={name !== optimisticName} />
      </p>
    </form>
  );
}

useOptimistic 用来维护临时状态,保证 UI 的乐观更新。

const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);

返回参数含义:

  • optimisticState:乐观更新的状态,UI 上应该始终消费这个状态。默认等于真正的 state。
  • addOptimistic:更新 optimisticState,可以通过 updateFn 指定更新逻辑

传入参数含义:

  • state:真正的状态
  • updateFn: (currentState, optimisticValue) => newOptimisticState,调用 addOptimistic 的时候会通过这个函数生成新的 optimisticState

异步函数执行结束后,无论是成功还是失败,optimisticName 都会重置成和最新 state 一样。

关于乐观更新,我在日常开发中,经常会用到。经典的场景是收藏点赞场景,用户触发后,立即更新 UI,如果请求失败后,再回滚 UI。

function LikeDemo() {
  const [like, setLike] = useState(false);
  const [pending, startTransition] = useTransition();
  const [optimisticLike, setOptimisticLike] = useOptimistic(like);

  const handleLike = () => {
    const targetLike = !like;
    startTransition(async () => {
      try {
        setOptimisticLike(targetLike);
        await updateLike(like);
        setLike(targetLike);
      } catch (e) {
        console.error(e);
      }
    });
  };

  return (
    <div>
      <div onClick={handleLike}>{optimisticLike ? "收藏" : "未收藏"}</div>
    </div>
  );
}

其实我觉得这个 Hook 没多大用,我普通代码也可以实现这个乐观更新,而且更简单。

  const [like, setLike] = useState(false);

  const handleLike = async () => {
    try {
      setLike((s) => !s);
      await updateLike(like);
    } catch (e) {
      setLike((s) => !s);
    }
  };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值