成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

React 19 全覽,新特性進行一次深度的體驗學習

開發 前端
這篇文章,我會通過豐富的示例,演示 React 19 的新特性,以及相較于老版本的差異。同時會附上自己對部分新特性的評價,如有不對,煩請指正。

最近 React 發布了 V19 RC 版本,按照慣例,我們對 React 19 的新特性進行一次深度的體驗學習,以便盡快上手新特性。

這篇文章,我會通過豐富的示例,演示 React 19 的新特性,以及相較于老版本的差異。同時會附上自己對部分新特性的評價,如有不對,煩請指正。

本文所有示例代碼可以在這里查看:https://codesandbox.io/p/sandbox/react19-demo-lmygpv[2]

React 19 的最重要改動,是新增了幾個 Hook,均是針對 form 和異步網絡請求通用能力的封裝。有點類似 react-query 的 useQuery[3],或者 ahooks 的 useRequest[4]

在 React Hooks 中,最基本的網絡請求我們可能會這樣寫:

function BasicDemo() {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    try {
      await updateName(name);
      console.log("Name updated successfully");
    } catch (e) {
      setError(e.message);
    }
    setIsPending(false);
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

上面是一個最簡單的網絡請求示例,點擊按鈕后,請求 updateName 接口,同時維護了 isPending 和 error 兩個請求相關的狀態。

useTransition 支持異步函數

useTransition 是 React 18  新增的一個 Hook,主要用來標記低優先級更新,低優先級更新是可以被中斷的。在 React 18 中,useTransition 返回的 isPending 代表這次低優先級的更新正在等待中。

const [isPending, startTransition] = useTransition();

在 18 中,useTransition 返回的 startTransition 只支持傳遞同步函數,而在 19 中,增加了對異步函數的支持。通過這個特性,我們可以用來自動維護異步請求的 isPending 狀態。代碼如下:

export default function BasicDemo() {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      try {
        await updateName(name);
        console.log("Name updated successfully");
      } catch (e) {
        setError(e.message);
      }
    });
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

上述寫法有兩個好處:

  • 自動維護了 isPending 狀態。
  • 標記 updateName 觸發的更新為低優先更新,不會阻塞 UI 渲染。

useActionState 管理異步函數狀態

useActionState 是 React 19 新增的一個 Hook,用來管理異步函數,自動維護了 data、action、pending 等狀態。經過 useActionState 改造的代碼如下:

export default function ActionStateDemo() {
  const [name, setName] = useState("");

  // 接受一個異步請求函數,返回 [data、action、pending]
  const [error, handleSubmit, isPending] = useActionState(
    async (previousState, name) => {
      try {
        await updateName(name);
        console.log("Name updated successfully");
        return null;
      } catch (e) {
        console.log("error");
        return e.message;
      }
    }
  );

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button
        onClick={() => {
          startTransition(() => {
            handleSubmit(name);
          });
        }}
        disabled={isPending}
      >
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

上面代碼乍一看,感覺很熟悉,好像和大家經常用的 useRequest、useQuery、useSWR 等都差不多。我們通過 API 來仔細了解一下 useActionState。

const [state, formAction, isPending] = useActionState(fn, initialState, permalink?);

返回參數含義:

  • state:代表 fn 函數返回的內容,fn 未執行時,等于 initialState。
  • formAction:用來觸發 fn 函數執行,可以直接調用,也可以傳遞給 form 的 action 屬性。
  • isPending:fn 函數是否正在執行中。

傳入參數含義:

  • fn:一個異步函數,接受兩個參數 previousState和 formData。
  • previousState:代表上一次執行 fn 返回的內容,首次調用等于 initialState。
  • formData:代表調用 formAction 時傳遞的參數。
  • initialState:fn 沒執行時,默認的 state。
  • permalink:一個 URL 字符串,通常和服務端組件有關系。(表示暫時沒看懂干啥的)。

你可以能注意到了上面 demo 中使用了 startTransition 來包裹調用 handleSubmit。因為不用 startTransition 來包裹,useActionState 就沒用。

官方提供的 demo 是通過 form action 觸發的 handleSubmit,其內置了 startTransition ,所以不需要手動設置。

<form action={handleSubmit}></form>

有沒有覺得很難用?我是這么覺得的。

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。

這個 Hook API 看起來還是挺簡單的。

但是關于上面的 Demo 示例,我有個困惑:如果請求失敗了,是怎么讓狀態回滾呢?

經過測試,上面的代碼確實有失敗狀態回滾的能力。

其奧秘就是異步函數執行結束后,無論是成功還是失敗,optimisticName 都會重置成和最新 state 一樣。

也就是我們調用了 setName(newName),那 optimisticName 就變成新的狀態。如果沒調用,則變成之前的狀態。

關于樂觀更新,我在日常開發中,經常會用到。經典的場景是點贊場景,用戶點贊后,立即更新 UI 為點贊成功,如果請求失敗后,再回滾 UI。

使用 useOptimistic 后,其代碼如下:

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

  // 定義樂觀更新的狀態
  const [optimisticLike, setOptimisticLike] = useOptimistic(like);

  const handleLike = async () => {
    const targetLike = !like;
    try {
      // 請求之前,先把狀態更新到 optimisticLike
      setOptimisticLike(targetLike);
      await updateLike(like);
      // 成功之后,更新最終狀態
      setLike(targetLike);
    } catch (e) {
      console.error(e);
    }
  };

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

上面代碼看起來很簡單,但是沒用,會報錯。

Warning: An optimistic state update occurred outside a transition or action. To fix, move the update to an action, or wrap with startTransition.

意思是 optimistic state 的更新,必須包裹在 startTransition 里面。

根據告警再優化下代碼。

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>
  );
}

增加了 startTransition 后,功能可以正常使用。

為什么官方的示例代碼不用加 startTransition 呢?因為官方示例是通過 form 的 action 調用的,其默認內置了 startTransition。

體驗下來,我覺得這個 Hook,確實是沒啥用,我普通代碼實現個樂觀更新,更簡單。

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

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

上面我們介紹了 React 19 新增的幾個 Hook,不知道大家看下來什么感受?說說我個人的感受。

React 19 之前的 Hook,基本都是原子級別的,必要的,比如 useState、useEffect、useTransition等,沒有它就有些功能實現不了。

但 React 19 新增的幾個 Hook 明顯不是這樣的,而是更上層的封裝,并且和 form 耦合很嚴重。

我覺得在實際業務開發中,幾乎不會用到上述 Hook。

useFormStatus 獲取表單狀態

useFormStatus 是 React 19 新增的一個 Hook,主要用來快捷讀取到最近的父級 form 表單的數據,其實就是類似 Context 的封裝。

import { useFormStatus } from "react-dom";
import action from './actions';

function Submit() {
  const status = useFormStatus();
  return <button disabled={status.pending}>Submit</button>
}

export default function App() {
  return (
    <form action={action}>
      <Submit />
    </form>
  );
}
const { pending, data, method, action } = useFormStatus();

useFormStatus 能拿到父級最近的 form 的狀態:

  • pending:是否正在提交中。
  • data:表單正在提交的數據,如果 form 沒有被提交,則為 null。
  • method:form 的 method 屬性,get 或 post。
  • action:form 的 action 屬性,如果 action 不是函數,則為 null。

useFormStatus 使用場景較窄,絕大部分開發者不會用到。

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} />;
};

未來在某個版本會刪除掉 forwardRef。

Context

在 React 19 之前,我們需要使用 Context.Provider,比如:

import React, { createContext } from 'react';
 
const ThemeContext = createContext('');
 
function App({ children }) {
  return (
    <ThemeContext.Provider value="dark">
      {children}
    </ThemeContext.Provider>
  );
}

在 React 19 中,我們可以使用 Context來代替 Context.Provider了。

function App({ children }) {
  return (
    <ThemeContext value="dark">
      {children}
    </ThemeContext>
  );
}

未來在某個版本會刪除掉 Context.Provider。

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
    };
  }}
/>

useDeferredValue 增加了 initialValue 參數

useDeferredValue 現在增加了第二個參數 initialValue,指定初始化值。

const value = useDeferredValue(deferredValue, initialValue);

支持 Document Metadata

在之前,如果我們希望動態的在組件中指定 meta、title、link等文檔屬性,我們可能會這樣做:

  • 在 useEffect 中,通過 JS 手動創建
  • 使用 react-helmet 這類三方庫

在 React 19 中,原生支持了這三個文檔屬性,支持在組件中設置。

在渲染過程中,React 發現這三種標簽,會自動提升到  上。

function BlogPost({post}) {
  return (
    <div>
      <meta name="author" content="Josh" />
      <link rel="author"  />
      <meta name="keywords" content={post.keywords} />
    </div>
  );
}

其它更多特性

  • Server Components 和 Server Actions 將成為穩定特性
  • 更多外聯樣式表能力支持:比如支持通過 precedence指定樣式表的優先級,同樣優先級的樣式表會被放到一起
  • 更多 script 標簽能力支持
  • 支持預加載資源

參考資料

[1]React 18 全覽: https://github.com/brickspert/blog/issues/48。

[2]https://codesandbox.io/p/sandbox/react19-demo-lmygpv: https://codesandbox.io/p/sandbox/react19-demo-lmygpv。

[3]useQuery: https://tanstack.com/query/latest/docs/framework/react/overview。

[4]useRequest: https://ahooks.js.org/hooks/use-request/index。

責任編輯:姜華 來源: 前端技術磚家
相關推薦

2024-12-20 16:31:34

2011-05-26 10:27:37

Fedora 15

2013-10-18 14:23:21

Ubuntu 13.1Kbuntu 13.1

2021-11-01 19:49:55

React組件模式

2024-04-28 09:01:06

React 19更新前端

2022-04-18 08:57:32

React 18前端

2010-07-20 10:19:06

Wine 1.2

2011-12-16 09:24:53

JavaSpring開源框架

2013-11-26 10:00:09

VMware Hori虛擬化Wmware

2013-11-26 10:00:30

VMware Hori

2024-06-28 11:39:21

2019-04-25 16:03:00

深度學習編程人工智能

2012-01-12 16:16:20

Fedora 17特性規劃

2010-06-01 09:52:27

jQuery Tool

2017-09-22 14:04:33

前端Node.js新特性

2010-01-26 17:44:32

Visual C++開

2022-06-27 06:02:27

geopandas開發Python

2023-11-23 10:21:11

ECMAScriptJavaScript

2021-04-07 10:13:51

人工智能深度學習

2021-12-09 22:22:19

Windows 11操作系統微軟
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久中文字幕一区 | 国产一区二区三区www | 午夜精品久久久久久久星辰影院 | 美女视频h | 精品自拍视频 | 蜜月va乱码一区二区三区 | 爱草在线 | 欧美国产精品久久久 | 在线观看国产www | 国产精品乱码一二三区的特点 | 欧美视频 | 中文字幕一区二区三区不卡在线 | 97国产精品 | 精品国产综合 | www国产亚洲精品久久网站 | 亚洲乱码一区二区三区在线观看 | 亚洲毛片网站 | av在线免费观看不卡 | 日本中文字幕在线视频 | 国产在线精品一区 | 久久夜视频 | 国产亚洲精品美女久久久久久久久久 | 成人黄视频在线观看 | 美女久久 | 日本高清aⅴ毛片免费 | 欧美在线观看一区 | 影音先锋中文字幕在线观看 | 欧美不卡 | 天天曰天天曰 | 特黄特黄a级毛片免费专区 av网站免费在线观看 | 罗宾被扒开腿做同人网站 | 久www | 日本 欧美 三级 高清 视频 | 国产精品一区二区精品 | 成人免费网站 | 国产在线视频一区二区 | 日韩视频一区二区三区 | 久久精品免费 | 亚洲一区视频在线播放 | www久久国产 | 欧美日韩一区二区在线播放 |