当前位置: 首页 > web >正文

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 函数,在组件卸载时会调用该函数。

<inputref={(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");// 请求之前,先把状态更新到 optimisticLikesetOptimisticName(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);}};

http://www.xdnf.cn/news/2981.html

相关文章:

  • CosyVoice、F5-TTS、GPT-SoVITS、Fish-Speech声音模型项目深度对比:选型指南
  • Linux 下编译BusyBox
  • SVMSPro平台获取Websocket视频流规则
  • 设计模式每日硬核训练 Day 17:中介者模式(Mediator Pattern)完整讲解与实战应用
  • 顺序结构双链表的实现
  • 【LLM】解析RAG增强检索技术:原理、实现与应用
  • 【HCIA】VRRP
  • LeetCode 2906 统计最大元素出现至少K次的子数组(滑动窗口)
  • 软件测试基础知识详解
  • 【AI面试准备】负责所有Al产品的模型能力评估及测试,保障AI产品的质量
  • AI Agent Protocols:现状、挑战与未来展望
  • 使用VS2022开发并部署QT应用
  • Karmada 多 Kubernetes集群管理实战
  • 如何查看和验证AWS CloudFront的托管区域ID
  • unity在编辑器模式调试音频卡顿电流声
  • 什么是向量库和数据向量化?建设向量库有什么作用?
  • vue.js中的一些事件修饰符【前端】
  • Pytest中的fixture装饰器详解
  • OpenCV 图形API(72)图像与通道拼接函数-----根据指定的方式翻转图像(GMat)函数 flip()
  • 布局元素组件 (Layout Element)
  • 功放IC搭配的升压芯片选型指南:为何FP5207更适合高保真功放系统?
  • 基于大模型的大肠息肉全程管理研究报告
  • 东土科技NewPre系列智能控制器的创新之旅
  • 第17节:传统分类模型-随机森林与决策树
  • 【Prometheus-Mongodb Exporter安装配置指南,开机自启】
  • 【安全扫描器原理】ICMP扫描
  • Docker基础(安装和命令)
  • 第三节:用户和用户组管理
  • 测试——BUG篇
  • python类中的 __contains__方法是什么?