初探 React Suspense

959 阅读3分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

什么是 React Suspense?

React Suspense 是组件在从缓存加载数据时暂停渲染的通用方法。支持以下功能:

  • 允许在“后台”渲染组件树
  • 当组件正在获取数据时,在整棵树准备好后才显示它们。

初探 React Suspense

尝试 React Suspense 的一个障碍是需要“伪造”后端 API 来与之交互,或者使用带有限制性 API 密钥的公共 API,这些 API 密钥会添加注册步骤和安全问题。

可以在本地运行Netlify Functions,实质上是在本地前端开发服务器旁边设置一个可部署的无服务器后端。这可以在开发时使用无服务器 Netlify 函数,当它们在生产中部署在 Netlify 上时,它们的工作方式相同。(作为一个额外的好处,将你的无服务器功能签入与你的前端相同的 Git 存储库中,允许原子部署——这意味着你的端点与你的客户端一起更新!)

拉取仓库

git clone https://github.com/netlify/create-react-app-lambda.git
cd create-react-app-lambda
yarn

运行yarn start,访问localhost:3000. 它看起来像create-react-appv2,但它已被修改,因此如果点击按钮,将向 指定的端点发出请求src/lambda/hello.js花一些时间熟悉src/Appsrc/lambda代码

转换为并发模式

升级 React 版本或安装react-cache

yarn add react-cache
// src/App.js
import React, {
  unstable_ConcurrentMode as ConcurrentMode, // new
  unstable_Suspense as Suspense, // new
  Component
} from 'react';
// ...
// wrap the JSX in line 38-46 in <ConcurrentMode>
// ...
class App extends Component {
  render() {
    return (
      <ConcurrentMode>
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Edit <code>src/App.js</code> and save to reload.
            </p>
            <Suspense maxDuration={1000} fallback={'Loading...'}>
              <LambdaDemo />
            </Suspense>
          </header>
        </div>
      </ConcurrentMode>
    );
  }
}

可以看到,在<ConcurrentMode>边上,添加了一个<Suspense>组件maxDuration={1000}和一个简单的后备 UI'Loading...'来包装我们的中心<LambdaDemo />组件。所有使用资源获取器的组件都必须包装在这样的Suspense组件中,以确保在暂停和maxDuration超出渲染时有一个回退 UI 。

创建缓存和资源

react-cache是一个由 React 团队维护的新库,作为可以与 React Suspense 一起使用的缓存的参考实现。替代缓存将由社区实施,包括 Facebook Relay 和Apollo GraphQL团队。

react-cache本身公开了两个用于非常一般用例的低级 API,因此通常更容易将 React Suspense 缓存初始化为单例并自动将其传递给资源。创建一个新src/cache.js文件并像这样填充它:

// src/cache.js
import { createCache, createResource } from 'react-cache';
export let cache;
function initCache() {
  cache = createCache(initCache);
}
initCache();
export function createFetcher(callback) {
  const resource = createResource(callback);
  return {
    read(...args) {
      return resource.read(cache, ...args);
    }
  };
}

src/App.js我们用25行代码写一个类组件来处理负载状态,处理点击,平我们/.netlify/functions/hello的端点,并显示来自后端的信息:

// currently inside src/App.js
class LambdaDemo extends Component {
  constructor(props) {
    super(props);
    this.state = { loading: false, msg: null };
  }
  handleClick = e => {
    e.preventDefault();
    this.setState({ loading: true });
    fetch('/.netlify/functions/hello')
      .then(response => response.json())
      .then(json => this.setState({ loading: false, msg: json.msg }));
  };
  render() {
    const { loading, msg } = this.state;
    return (
      <p>
        <button onClick={this.handleClick}>
          {loading ? 'Loading...' : 'Call Lambda'}
        </button>
        <br />
        <span>{msg}</span>
      </p>
    );
  }
}

使用的createFetcher实用程序,我们可以删除所有内容并将其替换为:

// new code in src/App.js
import { createFetcher } from './cache';
const DataFetcher = createFetcher(() =>
  fetch(`/.netlify/functions/hello`).then(x => x.json())
);
function LambdaDemo() {
  const msg = DataFetcher.read().msg;
  return <p>{msg}</p>;
}

在这里你已经编写了你的​​第一个 fetcher,它是一个带有缓存的资源,带有一个回调函数,我们用浏览器fetchAPI 返回一个promise。

DataFetcher被读取时,它会暂停LambdaDemo组件的渲染。挂起时,在组件中指定的fallback文本显示,当promise 解决时,完成其渲染,我们得到最终的 UI:'Loading...'``App``fetch``LambdaDemo