Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Asynchronously resolve data for your components, with support for server side rendering.

License

NotificationsYou must be signed in to change notification settings

ctrlplusb/react-jobs

Repository files navigation

Asynchronously resolve data for your components, with support for server side rendering.

npmMIT LicenseTravisCodecov

exportdefaultwithJob({work:(props)=>fetch(`/categories/${props.categoryID}`).then(r=>r.json()),LoadingComponent:(props)=><div>Loading...</div>,// OptionalErrorComponent:({ error})=><div>{error.message}</div>,// Optional})(Category)

TOCs

Introduction

This library provides you with a generic mechanism of attaching jobs to asynchronously resolve data for your React Components.

Features

  • Asynchronously resolve data for your components.
  • Show a LoadingComponent whilst data is being fetched.
  • Show an ErrorComponent if data fetching fails.
  • Simplefunction andPromise based API which allows you to easily compose additional features such as caching or 3rd party integrations (e.g. Redux).
  • Separate data loading concerns from your components to ease testing.
  • Support for server sider rendering applications, with:
    • data preloading on the server.
    • "job" deferring (i.e. insist that job only gets resolved in the browser).
    • rehydration API for the browser/client to prevent React checksum issues.
    • provides interoperability withreact-async-component for your code splitting needs.

Installation

npm

npm i react-jobs -S

yarn

yarn add react-jobs

Usage

In the naive example below we will use thefetch API (you may need topolyfill it for older browsers) to retrieve data from an API endpoint.

importReactfrom'react'import{withJob}from'react-jobs'// 👈importProductfrom'./Product'// When the work has completed your component will be rendered// with a "jobResult" prop containing the result of the work.//                               👇functionProducts({ categoryID, jobResult}){return(<div>{jobResult.map(product=><Productkey={product.id}product={product}/>)}</div>)}// You use the "withJob" function to attach work to your component.//             👇exportdefaultwithJob({work:(props)=>fetch(`/products/category/${props.categoryID}`).then(response=>response.json())})(Products)

This component can then be used like so:

<ProductscategoryID={1}/>

API

withJob(config)

Attaches a "job" to a target Component.

When the job has completed successfully your component will be rendered and provided ajobResult prop containing the result of the job.

Arguments

  • config (Object) : The configuration object for the async Component. It has the following properties available:
    • work ((props) => Promise|Result): A function containing the actual work that needs to be done for a job. It will be provided the props that are given to your component. It can return aPromise to asynchronously resolve the result of the job, or anything else in order to resolve synchronously.
    • LoadingComponent (Component, Optional, default:null) : A Component that will be displayed until thework is complete. All props will be passed to it.
    • ErrorComponent (Component, Optional, default:null) : A Component that will be displayed if any error occurred whilst trying to execute thework. All props will be passed to it as well as anerror prop containing theError.
    • shouldWorkAgain ((prevProps, nextProps, jobStatus) => boolean, Optional, default:null): A function that is executed with everycomponentWillReceiveProps lifecycle event. It receives the previous props, next props, and ajobStatus object. If the function returnstrue then thework function will be executed again, otherwise it will not. If this function is not defined, then the work will never get executed for anycomponentWillReceiveProps events. ThejobStatus object has the following members:
      • completed (Boolean): Has the job completed execution?
      • data (Any): The result if the job succeeded, else undefined.
      • error (Error): The error if the job failed, else undefined.
    • serverMode (Boolean, Optional, default:'resolve') : Only applies for server side rendering applications. Please see the documentation on server side rendering. The following values are allowed.
      • 'resolve' - Thework will be executed on the server.
      • 'defer' - Thework will not be executed on the server, being deferred to the browser.

Important notes regarding behaviour

Thework will fire under the following conditions:

  • Any timecomponentWillMount fires. i.e. any time your component mounts. If your component is mounted and then remounted later, itwill execute the work again. You may want work to only be executed once, in which case I suggest you store your work result in a cache or state management system such asredux. You can then check to see if the result exists in cache/state and resolve the existing value rather than perform a fetch for data.

OR

  • Any time thecomponentWillReceiveProps fires ANDshouldWorkAgain returnstrue.

Returns

A React component.

Examples

Asynchronous
exportdefaultwithJob({work:(props)=>newPromise('/fetchSomething')})(YourComponent);
Synchronous
exportdefaultwithJob({work:(props)=>'foo'})(YourComponent);
UsingshouldWorkAgain
exportdefaultwithJob({work:({ productId})=>getProduct(productId),shouldWorkAgain:function(prevProps,nextProps,jobStatus){// We will return true any time the productId changes// This will allow our work to re-execute, and the// appropriate product data can then be fetched.returnprevProps.productId!==nextProps.productId;}})(YourComponent);
Naive Caching
letresultCache=null;exportdefaultwithJob({work:(props)=>{if(resultCache){returnresultCache;}returnnewPromise('/fetchSomething').then((result)=>{resultCache=result;returnresult;});}})(YourComponent);
Retrying work that fails

You could use something like @sindresorhus'sp-retry within your work.

importpRetryfrom'p-retry';exportdefaultwithJob({work:({ productId})=>{construn=()=>fetch(`https://foo.com/products/${productId}`).then(response=>{// abort retrying if the resource doesn't existif(response.status===404){thrownewpRetry.AbortError(response.statusText);}returnresponse.json();});returnpRetry(run,{retries:5}).then(result=>{});}})(YourComponent);

Server Side Rendering

This library has been designed for interoperability withreact-async-bootstrapper.

react-async-bootstrapper allows us to do a "pre-render parse" of our React Element tree and execute anasyncBootstrap function that are attached to a components within the tree. In our case the "bootstrapping" process involves the resolution of our jobs prior to the render on the server. We use this 3rd party library as it allows interoperability with other libraries which also require a "bootstrapping" process (e.g. code splitting as supported byreact-async-component).

Firstly, installreact-async-bootstrapper:

npm install react-async-bootstrapper

Now, let's configure the "server" side. You could use a similarexpress (or other HTTP server) middleware configuration:

importReactfrom'react'import{JobProvider,createJobContext}from'react-jobs'// 👈importasyncBootstrapperfrom'react-async-bootstrapper'// 👈import{renderToString}from'react-dom/server'importserializefrom'serialize-javascript'importMyAppfrom'./shared/components/MyApp'exportdefaultfunctionexpressMiddleware(req,res,next){//    Create the job context for our provider, this grants// 👇 us the ability to track the resolved jobs to send back to the client.constjobContext=createJobContext()// 👇 Ensure you wrap your application with the provider.constapp=(<JobProviderjobContext={jobContext}><MyApp/></JobProvider>)// 👇 This makes sure we "bootstrap" resolve any jobs prior to renderingasyncBootstrapper(app).then(()=>{// We can now render our app 👇constappString=renderToString(app)// Get the resolved jobs state. 👇constjobsState=jobContext.getState()consthtml=`        <html>          <head>            <title>Example</title>          </head>          <body>            <div>${appString}</div>            <script type="text/javascript">              // Serialise the state into the HTML response              //                                 👇              window.JOBS_STATE =${serialize(jobsState)}            </script>          </body>        </html>`res.send(html)});}

Then on the "client" side you would do the following:

importReactfrom'react'import{render}from'react-dom'import{JobProvider}from'react-jobs'importMyAppfrom'./shared/components/MyApp'// Get any "rehydrate" state sent back by the server//                               👇constrehydrateState=window.JOBS_STATE// Surround your app with the JobProvider, providing// the rehydrateState//     👇constapp=(<JobProviderrehydrateState={rehydrateState}><MyApp/></JobProvider>)// Render 👍render(app,document.getElementById('app'))

FAQs

Let me know if you have any questions.

About

Asynchronously resolve data for your components, with support for server side rendering.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp