Suspense is one of the lesser-used features, released back in 2018 with version 16.6.0. Some of its related uses are more mature, while others are less stable, and have even undergone renaming and deletion.

Let’s take a look at its main uses and scenarios.

Help out code spliting with lazy

Import is a use of code spliting in Webpack, but the import file returns a promise that must be wrapped before it can be used, such as the react-loadable wrapper method

function Loadable(opts) { const { loading: LoadingComponent, loader } = opts return class LoadableComponent extends React.Component { constructor(props) { super(props) this.state = Loading: true, // Loaded: }} componentDidMount() {loader.componentDidmount ().then((loaded) => {this.setState({loading: false, loaded }) }) .catch(() => {}) } render() { const { loading, Loaded} = this.state if (loading) {return <LoadingComponent />} else if (loaded) {// Default loaded component const LoadedComponent = loaded.__esModule ? loaded.default : loaded; return <LoadedComponent {... this.props}/> } else { return null; }}}}Copy the code

Updating the component after the promise returns will be more elegant if you use suspense to overwrite react-loadable

const ProfilePage = React.lazy(() =>  import('./ProfilePage'));


<Suspense fallback={<Spinner />}>

  <ProfilePage />

</Suspense>
Copy the code

2.1 Solving loading Problems when Requesting data

let status = "pending"; let result; Const data = new Promise(resolve => setTimeout(() => resolve(" result "), 1000)); function wrapPromise(promise) { let suspender = promise.then( r => { status = "success"; result = r; }, e => { status = "error"; result = e; }); if (status === "pending") { throw suspender; } else if (status === "error") { throw result; } else if (status === "success") { return result; } } function App(){ const state = wrapPromise(data); return (<div>{state}</div>); } function Loading(){ return <div>.. loading</div> } class TodoApp extends React.Component { render() { return ( <React.Suspense fallback={<Loading></Loading>}> <App /> </React.Suspense> ) } } ReactDOM.render(<TodoApp />, document.querySelector("#app"))Copy the code

In suspense, when asking for data state in component App, throw Promise is returned at first. This is to make suspense catch error and return loading component. The way to write it depends on how suspense is implemented

class Suspense extends React.Component { 
    state = { promise: null } 
    componentDidCatch(e) { 
        if (e instanceof Promise) { 
            this.setState(
            { promise: e }, () => { 
                e.then(() => { 
                    this.setState({ promise: null }) 
                }) 
            }) 
        } 
    } 
    render() { 
        const { fallback, children } = this.props 
        const { promise } = this.state 
        return <> 
            { promise ? fallback : children } 
        </> 
    } 
}
Copy the code

Suspense source code shows that suspense catches an error, listens for it, and changes loading to a component in children when it returns a value. However, a component rendering is triggered again, so you need to cache the result of the request and end up writing it as above. Here’s an official example, a portal

2.2 Using React Cache

The example above is so anti-human that it would be almost impossible to write this in a real project, and react-cache would be much more elegant

import React, { Suspense } from "react"; import { unstable_createResource as createResource } from "react-cache"; const mockApi = () => { return new Promise((resolve, reject) => { setTimeout(() => resolve("Hello"), 1000); }); }; const resource = createResource(mockApi); const Greeting = () => { const result = resource.read(); return <div>{result} world</div>; }; const SuspenseDemo = () => { return ( <Suspense fallback={<div>loading... </div>}> <Greeting /> </Suspense> ); }; export default SuspenseDemo;Copy the code

React-cache is currently officially not recommended for use in online projects

3. Cooperate with ConcurrentMode to solve the flash problem of loading

Loading flash problem is mainly due to the short time of API interface, loading should not occur, and the interface speed needs to be judged

Suspense can be achieved in the usual way

const timeout = ms => new Promise((_, r) => setTimeout(r, ms)); const rq = (api, ms, resolve, reject) => async (... args) => { const request = api(... args); Promise.race([request, timeout(ms)]).then(resolve, err => { reject(err); return request.then(resolve); }); };Copy the code

Suspense provides us with the maxDuration property to control when loading is triggered

import React from "react"; import ReactDOM from "react-dom"; const { unstable_ConcurrentMode: ConcurrentMode, Suspense, } = React; const { unstable_createRoot: createRoot } = ReactDOM; let status = "pending"; let result; Const data = new Promise(resolve => setTimeout(() => resolve(" result "), 3000)); function wrapPromise(promise) { let suspender = promise.then( r => { status = "success"; result = r; }, e => { status = "error"; result = e; }); if (status === "pending") { throw suspender; } else if (status === "error") { throw result; } else if (status === "success") { return result; } } function Test(){ const state = wrapPromise(data); return (<div>{state}</div>); } function Loading(){ return <div>.. loading</div> } class TodoApp extends React.Component { render() { return ( <Suspense fallback={<Loading></Loading>} maxDuration={500}> <Test /> </Suspense> ) } } const rootElement = document.getElementById("root"); createRoot(rootElement).render( <ConcurrentMode> <TodoApp /> </ConcurrentMode> );Copy the code

The source address is here

The example above uses version 16.8.0

The example uses the unstable_ConcurrentMode and unstable_createRoot syntax. Unstable_createRoot was renamed to createRoot in 16.11.0. Unstable_ConcurrentMode was renamed unstable_createRoot in 16.9.0. Tests in the latest 16.13.1 found that ReactDOM. CreateRoot does not exist, so this example will only be tested in 16.8.0

conclusion

These are all scenarios for Suspense, the API is currently unstable, so use it with caution

recruitment

Recently, if you are interested in the front end of bytedance, please send a private letter to me, or send my email [email protected] front end base Shanghai, Beijing, Nanjing, Shenzhen, Hangzhou, job requirements can refer to job.toutiao.com/s/7wokvh in addition to the front end of other positions are also welcome to send