React 的將來,與 Suspense 同行

做者:Lusan Dascss

翻譯:瘋狂的技術宅html

原文:blog.logrocket.com/the-future-…前端

未經容許嚴禁轉載react

自從 React 團隊發佈他們的 16.x 願景以來,已經風靡了整個社區。它添加了一些很是酷的東西:Hooks、惰性加載、Suspense 和緩存 API。webpack

這不是又一篇關於如何編寫 Hook 的文章,而是對 React 將來的期待!若是你從沒有據說過 React Hooks 或其餘新的 API,那麼本文將會讓你對 React 的將來感到興奮。git

隨着文章的深刻,咱們將介紹兩個新概念,預計它們將會在 2019 年第二季度發佈:github

  • 如何使用 Suspense 獲取數據
  • 如何使用 react-cache

我已經很興奮了!可是在咱們進行深刻探討以前,先來快速回顧一下。web

React Hooks

在 React 16.8 中,Hooks 正式成爲穩定版本的一部分。它在高層次上解決了一些問題:編程

  • 因爲有了用函數編寫全部內容的概念,使得編寫的代碼更加模塊化,更易於維護
  • 不鼓勵使用 HOCs 和其餘使代碼難以理解的複雜功能
  • 放棄使用複雜的生命週期,如 componentDidMountcomponentDidUpdate 等,這會使咱們寫重複的代碼

若是你想更詳細地瞭解這些,請查看此處前端工程化

那麼,讓咱們看一下 React Hooks 的演示以及典型應用的外觀!

CodeSandbox上的演示:codesandbox.io/embed/3rm5j…

React.lazy

這個名字真的是暴露了它的意圖!當咱們想對組件進行惰性加載時會須要它:

const TodoList = React.lazy(() => import("./containers/todoList"));
複製代碼

在 webpack 動態導入的幫助下就能夠作到這些,它有助於建立 bundles ,從而提升頁面的加載速度。讓咱們作一個演示!回到前面 Codesandbox 的演示連接並將導入更改成如下內容:

const TodoList = React.lazy(() => import("./containers/todoList"));
const CompletedList = React.lazy(() => import("./containers/completedList"));
const AddNewTask = React.lazy(() => import("./containers/addNewTask"));
複製代碼

請注意下圖中獨立的 bundle 是如何建立的!😄

用 Webpack 建立的Bundle
使用 Webpack 建立的Bundle

Suspense

Suspense 用起來至關簡單。下面的代碼能夠幫你更好地理解這一點:

// https://codesandbox.io/s/new-6m2gj
import React, { useState, useEffect, Suspense } from "react";
import ReactDOM from "react-dom";
import todoListData from "./containers/todoList/todolistData";
import Header from "./containers/header";
import Clock from "./components/Clock";
import "./styles.css";

const TodoList = React.lazy(() => import("./containers/todoList"));
const CompletedList = React.lazy(() => import("./containers/completedList"));
const AddNewTask = React.lazy(() => import("./containers/addNewTask"));

function App() {
  const { todolist } = todoListData;
  const [todoListPayload, setTodoListPayload] = useState(todolist);
  const [completedTodoList, setCompletedTodoList] = useState([]);

  const addTodoTaskHandler = value => {
    // addTodoTaskHandler
  };

  const removeTodoTaskHandler = ({ id }) => {
    // Remove from the set of todo list
  };

  const completeTodoTaskHandler = ({ id }) => {
    // Get item to remove
  };

return (
    <div className="App">
      <Header title={"My Tasks"} />
      <Clock />
      <div className="PageLayout">
        <Suspense fallback={<div>Loading...</div>}>
          <TodoList
            payload={todoListPayload}
            completeTodoTaskHandler={completeTodoTaskHandler}
          />
          <CompletedList list={completedTodoList} />
          <AddNewTask addTodoTaskHandler={addTodoTaskHandler} />
        </Suspense>
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
複製代碼

若是你想運行它的話,能夠訪問這個演示連接

若是咱們檢查演示中的代碼,就會看到:

<Suspense fallback={<div>Loading...</div>}>
  <TodoList
     payload={todoListPayload}
     completeTodoTaskHandler={completeTodoTaskHandler}
   />
  <CompletedList list={completedTodoList} />
  <AddNewTask addTodoTaskHandler={addTodoTaskHandler} />
</Suspense>
複製代碼

它就像用 Suspense 包裝組件同樣簡單。咱們用 React.lazy() 懶加載了一些組件: TodoListCompletedListAddNewTask。因爲在內部它分別爲每一個組件生成 bundle,所以在網絡較慢的條件下,可能須要一些時間去加載它們。

Suspense 將經過顯示 fallback (例如 Loading ...)或其餘任何組件(如 spinner 或相似組件)來自動處理。

深刻了解 React 的將來

前面的簡短回顧已經使人很是激動了。如今讓咱們爲 Suspense 帶來更多樂趣。

Suspense 和 react-cache

等等,咱們尚未講到 Suspense ?那麼若是我告訴你 Suspense 在調用 API 時也能夠處理咱們的加載狀態呢?不過在此以前咱們須要深刻研究並更好地理解它。

通過一番挖掘和研究,我終於找到了 Shawn Swyx Wang🌟 的 GitHub 存儲庫,我想直接引用他的文檔

React Suspense 是組件在從緩存加載數據時暫停渲染的通用方法。

它解決了當渲染是 I/O 綁定時的問題。

好的,「從緩存加載數據」給了我一個提示,但我須要更多關於如何真正處理API的信息。

Kent C. Dodds在他的 Egghead 課程中講授了一個重要概念:若是咱們拋出一個 promise,Suspense 就會自動知道已經調用了一個 API 請求。

import React, { Suspense } from "react";

fetchArticles() {
  // Some fetch API fetching articles
}

let isRequestCalled = false;
function Content() {
  let result = [];
  if (!cache) {
    const promise = fetchArticles();
    isRequestCalled = true;
    throw promise; // Let suspense know
  }
  return <div>Article</div>;
}

const Articles = () => {
  return (
    <div> {/* Yay promise is thrown */} <Suspense fallback={<div>loading...</div>}> <Content /> </Suspense> </div>
  );
};

export default Articles;
複製代碼

固然,這不是處理代碼的最佳方式;它看起來有點麻煩。所以讓咱們試着用 react-cache 來更好地處理這些代碼:

import React, { Suspense } from "react";

import { unstable_createResource as createResource } from "react-cache";

function fetchArticles() {
  // Some fetch API fetching articles
}

const politicalArticles = createResource(fetchArticles);

function Content() {
  const result = politicalArticles.read(someKey);
  return <div>Article</div>;
}

const Articles = () => {
  return (
    <div> <Suspense fallback={<div>loading...</div>}> <Content /> </Suspense> </div>
  );
};

export default Articles;
複製代碼

來自 react-cache 的 createResource 從回調中建立資源,並返回一個 promise。

好吧,爲了讓 Suspense 知道它必須顯示加載狀態,所須要的只是一個 promise。在 promise 解決以前,它將繼續顯示加載狀態。

可是,這是實驗性的。我相信你會遇到錯誤,因此不要擔憂,很明顯 react-cache 仍處於開發階段。

一點要當心,確保在組件內部使用 read 方法,不然,它會拋出一個錯誤。

// A snippet from the React-cache library

function readContext(Context, observedBits) {
  const dispatcher = ReactCurrentDispatcher.current;
  if (dispatcher === null) {
    throw new Error(
      'react-cache: read and preload may only be called from within a ' +
        "component's render. They are not supported in event handlers or " +
        'lifecycle methods.',
    );
  }
  return dispatcher.readContext(Context, observedBits);
}
複製代碼

若是你有興趣閱讀 react-cache 源代碼,請查看連接

恭喜!

如今咱們抓住了 React 的不久的未來,有一件事是顯而易見的:React 團隊但願儘量的簡化 API。

我也對愈來愈多的庫正朝着函數式編程的方向發展而感到興奮。這種模式確定會完全改變咱們編寫前端的方式。我也在關注併發的 React —— 若是你有興趣,請查看官方的路線圖文檔。 React-cache 和 Suspense 是併發 react 的一部分功能😎。

歡迎關注前端公衆號:前端先鋒,獲取前端工程化實用工具包。

相關文章
相關標籤/搜索