首頁  >  文章  >  web前端  >  新的 React 帶來了什麼 - 獲得清晰的了解

新的 React 帶來了什麼 - 獲得清晰的了解

Linda Hamilton
Linda Hamilton原創
2024-11-24 13:16:15357瀏覽

React 19 於 2024 年 4 月 25 日發布。 JavaScript 世界變化如此之快,有時會讓人感覺難以跟上。但是,當這些變更旨在讓您作為 React 開發人員的生活變得更輕鬆時,就值得一看,對吧?由於 React 是這個生態系統中如此重要的一部分,因此保持更新是必要的。

React 19 最好的部分是它專注於讓事情變得更簡單。這些更新旨在讓學習 React 變得更容易,讓您花更多時間進行創建,而不是處理棘手的設定。一些新功能確實改變了遊戲規則,可能會極大地改變您的工作方式,所以您絕對不想錯過它們。

我總是嘗試以易於理解的方式解釋事物,而不使用複雜的字詞。本文也不例外。我的目標是確保您清楚了解所有內容,所以讓我們一起探索 React 19 中的精彩更新!

請記住,React 19 還不太穩定。目前它被稱為 React Canary。因此,請記住,實際上並不建議將其用於生產。

反應編譯器

為了優化我們的 React 應用程序,我們使用一些內建方法,例如 useMemo、useCallback 或 memo。這告訴 React 如果輸入沒有改變就不要再編譯程式碼。但如果你忘記應用記憶化,就會導致浪費 React 資源和運算能力。為了解決這個問題,React 19 引進了 React Compiler。 React 的新編譯器是 19 版本新發布的亮點。新的編譯器會在幕後優化您的程式碼,因此您可以放棄這些鉤子並專注於編寫漂亮、乾淨的 React 元件。

簡而言之,您不需要使用 useMemo 或 useCallback 包裝函數來最佳化效能,也不需要使用 memo 包裝元件來防止重新渲染元件。

操作(useTransition 掛鉤)

我們來聊聊廢話吧? ! !你是否注意到在 React 19 發布之前 useTransition 鉤子幾乎沒有被提及?還是只有我這樣?嗯,至少這是我注意到的,尤其是對於初級開發人員來說。無論如何,讓我向您介紹一下它在先前版本中的工作原理,然後我們將了解為什麼現在它如此重要。

useTransition 傳回一個包含兩個元素的陣列:startTransition 函數和 isPending 布林值。您可以將狀態更新包裝在 startTransition 函數中,以將它們標記為轉換(優先順序較低的程式碼)。這意味著用 startTransition 包裹的部分在其他連續任務完成後開始工作。

在 React 18 中,startTransition 函數不直接支援非同步函數。這是一個限制,因為 startTransition 旨在將更新標記為低優先級,但無法原生處理非同步邏輯。

React 19 中,此限制已解決。現在,startTransition 支援非同步函數,這表示您可以在其中執行非同步任務(例如,資料擷取),同時將這些更新標記為轉換。此增強功能允許更靈活和直觀地使用 startTransition,使其感覺像是一項「新」功能,儘管它在技術上是對現有功能的改進。


按照慣例,使用非同步轉換的函數稱為「操作」。

例如,您可以在 useState 中處理掛起和錯誤狀態:

// Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/path");
  };

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

React 19 支援在轉換中使用非同步函數來自動處理待處理狀態、錯誤、表單和樂觀更新。例如,您可以使用 useTransition 來為您處理掛起狀態:

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

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

非同步轉換將立即將 isPending 狀態設為 true,發出非同步請求,並在任何轉換後將 isPending 切換為 false。這使您可以在資料變更時保持當前 UI 的回應能力和互動性。

行動

React 團隊增加了對傳遞函數作為操作的支援。

export default function App() {
    const [name, setName] = useState(
        () => JSON.parse(localStorage.getItem("name")) || "Anonymous user"
    )

    async function formAction(formData){
        try {
            const newName = await updateNameInDB(formData.get("name"))
            setName(newName)
        }
    }

    return (
        <>
            <p className="username">
                Current user: <span>{name}</span>
            </p>
            <form action={formAction}>
                <input
                    type="text"
                    name="name"
                    required
                />
                <button type="submit">Update</button>
            </form>
        </>
    )
}

formAction 函數(您可以命名任何名稱)在其參數中取得表單資料。每個欄位都由名稱屬性表示,因此在命名輸入時必須小心。 formData 參數其實就是本機 FormData Web API 物件。您可以在 mdn 網路文件上找到它。另一個好處是你不需要應用 event.preventDefault(),因為它是由 React 處理的。

當表單操作成功時,React 會自動重設表單。但如果你想重置

手動,您可以呼叫新的 requestFormReset React DOM API。

新鉤子:useActionState


React.useActionState 以前在 Canary 版本中稱為 ReactDOM.useFormState,但已重新命名並棄用 useFormState。

useActionState 追蹤元件狀態、掛起狀態,並提供包裝的操作函數,以在表單或我們可能想要執行突變的任何其他地方使用。

這是一個更描述性地分解它的例子:

import { useActionState } from "react"
import { updateNameInDB } from "../api/user"

export default function App() {
    const [user, formAction, isPending] = useActionState(insertName, {
        name: "John Doe",
        error: null
    })

    async function insertName(prevState, formData){
        try {
            const response = await updateNameInDB(formData.get("name"))
            return {name: response.name}
        }catch(error){
            return {...prevState, error: error.error}
        }
    }

    return (
        <>
            {user?.name && (
                <p>



<p>How this hook works is described with reference to the example:</p>

<ol>
<li><p>The first argument of the useActionState hook is the "Action" function, which is defined here as insertName.</p></li>
<li><p>The second argument is the initial state, which is accessible through the first element of the result array. In this example, the initial state includes name and error, and the state is represented as user in the component.</p></li>
<li><p>The insertName function returns the updated state. If the operation is successful, it updates the name property. If an error occurs, it updates the error property while preserving the rest of the previous state.</p></li>
<li>
<p>The result of the useActionState hook is an array with three elements:</p>

<ul>
<li>The current state (user): Reflects the latest state of the data.</li>
<li>A dispatchable function (formAction): Triggers the action when called, as seen in the form element's action attribute.</li>
<li>A pending state (isPending): Tracks whether the action is currently in progress, useful for managing transitions or loading indicators.</li>
</ul>
</li>
</ol>

<h2>
  
  
  New Hook: useOptimistic
</h2>

<p>When it’s performing a data mutation and to show the final state right after the user instructs (could be a tap on a button) or you could say optimistically while the async request is underway, you need to use this hook. Here is a demonstration how you can do this:<br>
</p>

<pre class="brush:php;toolbar:false">function ChangeName({currentName, onUpdateName}) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async (formData) => {
    const newName = formData.get("name");
    setOptimisticName(newName);
    const updatedName = await updateName(newName);
    onUpdateName(updatedName);
  };

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

useOptimistic 鉤子將在 updateName 請求正在進行時立即渲染 optimisticName。當更新完成時,React 會將更新後的值插入 currentName 中,或者如果更新出錯,React 會自動切換回 currentName 值。

新掛鉤:useFormStatus

useFormStatus 掛鉤可協助您追蹤表單提交。等一下 ? !這是跟踪非同步轉換的另一個鉤子嗎?答案是「是」和「否」。由於您已經了解了 useActionState 掛鉤,您可以說這是另一個用於追蹤非同步轉換的掛鉤。但 useFormStatus 不會導致任何操作發生,而是提供上次表單提交的狀態資訊。

// Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/path");
  };

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

嗯,我想說這裡要注意的最重要的事情是 useFormStatus hook 實際上來自 React-dom,而不是 React。

useFormStatus 讀取父級

的狀態;就好像表單是上下文提供者一樣。若要取得狀態訊息,必須在
內呈現 Submit 元件。 Hook 傳回諸如掛起屬性之類的信息,告訴您表單是否正在主動提交。

在上面的範例中,Submit 使用此資訊來停用

新API:使用

你可以使用 use 來讀取 Promise,React 將掛起元件直到 Promise 解決:

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

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

上面的範例示範了 use API 的用例。 「Comments」是一個使用名為「commentsPromise」的承諾的組件。該承諾被新的 use API 消耗,從而使其掛起元件。順便說一句,您必須使用 Suspense Fallback 來包裝組件。

使用 API 有一個您必須注意的限制。限制是每次使用包含元件的 API 重新渲染時,它都會創建另一個承諾,從而導致效能不佳。所以,基本上它沒有快取機制。以下是發布部落格中的謹慎說明:

What New React Has Brought to The Table - Get Clear Understanding

use API 也從上下文中讀取資料。例如,要讀取上下文值,我們只需將上下文傳遞給 use(),該函數就會遍歷元件樹以查找最近的上下文提供者。

與讀取上下文的 useContext() Hook 不同,use() 函數可以在我們元件的條件和循環中使用!

export default function App() {
    const [name, setName] = useState(
        () => JSON.parse(localStorage.getItem("name")) || "Anonymous user"
    )

    async function formAction(formData){
        try {
            const newName = await updateNameInDB(formData.get("name"))
            setName(newName)
        }
    }

    return (
        <>
            <p className="username">
                Current user: <span>{name}</span>
            </p>
            <form action={formAction}>
                <input
                    type="text"
                    name="name"
                    required
                />
                <button type="submit">Update</button>
            </form>
        </>
    )
}

ref 作為 prop(無forwardRef)

在 React 19 中,您可以像任何其他 prop 一樣傳遞 refs,這會導致 forwardRef 被棄用。這簡化了元件程式碼並使引用處理變得輕而易舉。 ?

import { useActionState } from "react"
import { updateNameInDB } from "../api/user"

export default function App() {
    const [user, formAction, isPending] = useActionState(insertName, {
        name: "John Doe",
        error: null
    })

    async function insertName(prevState, formData){
        try {
            const response = await updateNameInDB(formData.get("name"))
            return {name: response.name}
        }catch(error){
            return {...prevState, error: error.error}
        }
    }

    return (
        <>
            {user?.name && (
                <p>



<p>How this hook works is described with reference to the example:</p>

<ol>
<li><p>The first argument of the useActionState hook is the "Action" function, which is defined here as insertName.</p></li>
<li><p>The second argument is the initial state, which is accessible through the first element of the result array. In this example, the initial state includes name and error, and the state is represented as user in the component.</p></li>
<li><p>The insertName function returns the updated state. If the operation is successful, it updates the name property. If an error occurs, it updates the error property while preserving the rest of the previous state.</p></li>
<li>
<p>The result of the useActionState hook is an array with three elements:</p>

<ul>
<li>The current state (user): Reflects the latest state of the data.</li>
<li>A dispatchable function (formAction): Triggers the action when called, as seen in the form element's action attribute.</li>
<li>A pending state (isPending): Tracks whether the action is currently in progress, useful for managing transitions or loading indicators.</li>
</ul>
</li>
</ol>

<h2>
  
  
  New Hook: useOptimistic
</h2>

<p>When it’s performing a data mutation and to show the final state right after the user instructs (could be a tap on a button) or you could say optimistically while the async request is underway, you need to use this hook. Here is a demonstration how you can do this:<br>
</p>

<pre class="brush:php;toolbar:false">function ChangeName({currentName, onUpdateName}) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async (formData) => {
    const newName = formData.get("name");
    setOptimisticName(newName);
    const updatedName = await updateName(newName);
    onUpdateName(updatedName);
  };

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

React 團隊決定在即將推出的版本中棄用forwardRef。

反應伺服器元件

順便說一句,React 伺服器元件(RSC) 是一個支援伺服器端渲染的React 功能,但是像Next.js 這樣的框架已經接受了RSC,並將它們無縫整合到他們的工作流程中。如果你是這個生態系統的新手,請先弄清楚這一點,然後再開始研究它的機制。

React Server Components 是 React 19 中引入的一項新功能,它允許我們建立在伺服器上執行的無狀態 React 元件。

由於 React Server 元件能夠在 Web 伺服器上運行,因此它們可以用於存取資料層,而無需與 API 互動!

// Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/path");
  };

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

有了這個,我們不必公開 API 端點或使用額外的客戶端獲取邏輯將資料直接載入到我們的元件中。所有資料處理都在伺服器上完成。

請記住,伺服器元件在伺服器上運行,而不是在瀏覽器上運行。因此,他們無法使用傳統的 React 元件 API,例如 useState。為了在 React 伺服器元件設定引入互動性,我們需要利用客戶端元件來補充伺服器元件來處理互動性。

使用 React 伺服器元件時,「使用客戶端」表示該元件是用戶端元件,這意味著它可以管理狀態、處理使用者互動以及使用特定於瀏覽器的 API。該指令明確告訴 React 框架和捆綁器將該元件與伺服器元件區別對待,伺服器元件是無狀態的並在伺服器上運行。

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

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

另一方面,React 伺服器元件是預設的,因此我們不會在伺服器元件檔案的頂部聲明「使用伺服器」。相反,「use server」應該僅用於標記可以從客戶端元件呼叫的伺服器端函數。

export default function App() {
    const [name, setName] = useState(
        () => JSON.parse(localStorage.getItem("name")) || "Anonymous user"
    )

    async function formAction(formData){
        try {
            const newName = await updateNameInDB(formData.get("name"))
            setName(newName)
        }
    }

    return (
        <>
            <p className="username">
                Current user: <span>{name}</span>
            </p>
            <form action={formAction}>
                <input
                    type="text"
                    name="name"
                    required
                />
                <button type="submit">Update</button>
            </form>
        </>
    )
}

我認為這個例子很好地闡明了這一點。

結論

React 19 仍稱為 React Canary。因此,將其用於生產並不是一個好主意。但用 React 19 擁抱未來,讓你的開發體驗更順暢、更愉快。

以上是新的 React 帶來了什麼 - 獲得清晰的了解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn