Giter Club home page Giter Club logo

react-async-call's Introduction

React Async Call

npm version Build Status Code Coverage MIT License size gzip size module formats: umd, cjs, and es

Declarative promise handling in React.

Table of Content

Motivation

Handling promise-returning functions (for example, fetch) is not hard to do in React application. There are several packages like holen or React Request for simplifying such kind of tasks but theirs authors try to impose their own vision on various aspects of data fetching. For instance, React Request has a cache for HTTP requests but what will happen if you decide to change caching strategy? React Async Call tries to sharpen the edges. This is minimalistic package that can be used not only for data fetching but also for handling any promise-returning functions.

Install

Using npm

npm i react-async-call --save

Then, use it as usual:

// using ES6 modules
import createAsyncCallComponent from 'react-async-call'

// using CommonJS modules
var createAsyncCallComponent = require('react-async-call').createAsyncCallComponent

UMD build

The UMD build is also available on unpkg:

<script src="https://unpkg.com/react-async-call"></script>

The package is avalable on window.ReactAsyncCall

Usage

Basic Usage

import createAsyncCallComponent from 'react-async-call'

const AsyncCall = createAsyncCallComponent(value => Promise.resolve(42))

const Example = () => (
  <AsyncCall>
    <AsyncCall.Running>
      <div>Loading...</div>
    </AsyncCall.Running>
    <AsyncCall.Resolved>{({ result }) => <div>The result of function call is {result}</div>}</AsyncCall.Resolved>
    <AsyncCall.Rejected>{({ rejectReason }) => <div>Error: {rejectReason}</div>}</AsyncCall.Rejected>
  </AsyncCall>
)

Data Fetching

Incremental Data Fetching

API Reference

Exported Members

createAsyncCallComponent(fn, [displayName])AsyncCall

A factory function that creates React component class and binds async operation to it:

const Fetcher = createAsyncCallComponent(() => fetch('https://api.github.com/repositories').then(data => data.json()))

After calling of this function you can use returned component and its static sub-components to hook async operation lifecycle:

// Start executing async operation on Fetcher mount
<Fetcher>
  <Fetcher.Running>
    Renders only if async operation is executing
  </Fetcher.Running>

  <Fetcher.Resolved>
    {({ result }) => (
      <div>
        Renders if async operation has been executed successfully
        <pre>{JSON.stringify(result)}></pre>
      </div>
    )}}
  </Fetcher.Resolved>

  <Fetcher.Rejected>
    Renders only if async operation failed
  </Fetcher.Rejected>
</Fetcher>

createAsyncCallComponent is the only member exported by react-async-call package.

Kind: global function
Params

Param Type Default Description
fn AsyncFunction See AsyncFunction signature for details.
[displayName] String "AsyncCall" Component name (visible, for example, in React extension of Chrome Dev Tools).

Returns: AsyncCall - Returns React component class AsyncCall for the further usage. This class contains extra component classes Running, Rejected, Resolved, Completed, ResultStore, Executor and State which can be used as children (direct or indirect) of AsyncCall.


React Components

AsyncCall ⇐ React.Component

React Component. This class is returned by call of createAsyncCallComponent and this is the only way to get it.

Kind: global class
Extends: React.Component
Properties

Name Type Default Description
children ReactNode | AsyncCallChildrenFunction Property children can be either a ReactNode or a function that receives an object as its only argument and return ReactNode. AsyncCall component always renders its children. We recommend to use the first form of using children property and respond to async operation execution results using static sub-components (like Running, Rejected, Resolved, Completed, ResultStore, Executor and State).
params any A value that will be passed as a single argument into async function. Every time when change of this property occurs, asyncronous operation restarts.
[lazy] Boolean false If true, component will not start execution of asynchronous operation during component mount phase and on params property change. You should start async operation manualy by calling execute method or via Executor.

asyncCall.execute()

Method for executing async operation manually. It is recommended to use <Executor> component instead.

Kind: instance method of AsyncCall


AsyncCall.ResultStore ⇐ React.Component

React Component. Implements store of results of sequential async calls. Useful when you need to accumulate results of async calls (e.g., to glue together sequential calls of server API).

Kind: static class of AsyncCall
Extends: React.Component
Properties

Name Type Default Description
children ResultStoreChildrenFunction | ReactNode React children or function that returns rendered result depending on hasResult flag and result.
reduce ReduceFunction Function from previousResult and currentResult to a new result. Useful, for example, when you need to accumulate sequential async calls (e.g. for fetching data for infinte page scroll).
[initialValue] any Optional initial value for the result store. If value is provided, result store will have result always.
[reset] boolean false @deprecated If true, clears the store (Deprecated, will be removed in version 1.0.0. Use Resetter instead).

resultStore.reset([execute])

Resets result store to its initial state.

Kind: instance method of ResultStore
Params

Param Type Default Description
[execute] bool true Wether execute promise-returning function after resetting or not.

ResultStore.HasResult ⇐ React.StatelessComponent

React Component. Renders its children whenever result store is not empty (has result). Property children must be a function with the only argument receiving object with the only field result.

Kind: static class of ResultStore
Extends: React.StatelessComponent
Properties

Name Type
children HasResultChildrenFunction

ResultStore.Resetter ⇐ React.StatelessComponent

React Component. Renders its children always. Property children must be a function with the only argument receiving an object with a function for manual reset of ResultStore.

Kind: static class of ResultStore
Extends: React.StatelessComponent
Properties

Name Type
children ResetterChildrenFunction

AsyncCall.Running ⇐ React.StatelessComponent

React Component. Renders its children whenever async operation was started but is still executing. Otherwise renders nothing.

Kind: static class of AsyncCall
Extends: React.StatelessComponent
Properties

Name Type
children ReactNode

AsyncCall.Resolved ⇐ React.StatelessComponent

React Component. Renders its children whenever async operation has been completed successfully (promise was resolved), but is still not started again. Otherwise renders nothing. Property children can be either React node(s) or children function with the only argument receiving object with the only field result.

Kind: static class of AsyncCall
Extends: React.StatelessComponent
Properties

Name Type
children ReactNode | ResolvedChildrenFunction

AsyncCall.Rejected ⇐ React.StatelessComponent

React Component. Renders its children whenever async operation has been completed with failure (promise was rejected), but is still not started again. Otherwise renders nothing. Property children can be either React node(s) or children function with the only argument receiving object with the only field rejectReason (promise reject reason).

Kind: static class of AsyncCall
Extends: React.StatelessComponent
Properties

Name Type
children ReactNode | RejectedChildrenFunction

AsyncCall.Executor ⇐ React.StatelessComponent

React Component. Renders its children always. Property children must be a function with the only argument receiving an object with a function for manual execution of async operation.

Kind: static class of AsyncCall
Extends: React.StatelessComponent
Properties

Name Type
children ExecutorChildrenFunction

AsyncCall.State ⇐ React.StatelessComponent

React Component. Renders its children always. Property children must be a function with the only argument receiving an object (see description of StateChildrenFunction) with the state of async operation. State component is handy for complicated UI cases when none of static components of AsyncCall suits you.

Kind: static class of AsyncCall
Extends: React.StatelessComponent
Properties

Name Type
children StateChildrenFunction

AsyncCall.Completed ⇐ React.StatelessComponent

React Component. Renders its children whenever async operation has been completed (successfully or not), but is still not started again. Otherwise renders nothing.

<AsyncCall.Completed>
  Async operation completed
</AsyncCall.Completed>

Kind: static class of AsyncCall
Extends: React.StatelessComponent
Properties

Name Type Description
[children] ReactNode React children to be rendered whenever async operation is completed.

Function Signatures

ResultStoreChildrenFunction(params)ReactNode

Type of children function of a ResultStore component.

Params

Param Type Description
params object
params.hasResult boolean
[params.result] any
params.reset ResetFunction Function for manual store cleaning.

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


ReduceFunction(previousResult, currentResult)any

Type of reduce property of a ResultStore component.

Params

Param Type
previousResult any
currentResult any

HasResultChildrenFunction(params)ReactNode

Type of children function for HasResult

Params

Param Type
params object
params.result any

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


ResetFunction([execute])

Reset function

Params

Param Type Default Description
[execute] bool false Wether execute promise-returning function after resetting or not.

ResetterChildrenFunction(params)ReactNode

Type of children function for Resetter

Params

Param Type Description
params object
params.reset ResetFunction Function for manual clearing of ResultStore.

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


AsyncFunction(params)Promise

Asynchronous function (aka asynchronous operation or promise-returning function) which returns promise based on supplied parameter.

Params

Param Type Description
params any Parameters, based on which function should return promise.

Returns: Promise - Promise object that represents asynchronous operation result.
Example
The function below returns result (Promise object) of getting user data from GitHub API by his/her GitHub login:

const getGitHubUserData = userName => fetch(`https://api.github.com/users/${userName}`).then(data => data.json())

ExecuteFunction()void

Execute function


AsyncCallChildrenFunction(params)ReactNode

Type of children function for AsyncCall

Params

Param Type Description
params object Represents current status of asynchronous operation.
params.running boolean Indicates whether asynchronous operation is executing or not.
params.rejected boolean Indicates whether asynchronous operation was failed when it was called last time. If true, result of promise rejection (error) can be found in the params.rejectReason.
params.resolved boolean Indicates whether asynchronous operation was succeeded when it was called last time. If true, result of promise resolving can be found in the params.result.
[params.result] any Contains result of promise (returned by async function) resolving if function call was successful. undefined if asynchronous operation is running or promise was rejected.
[params.rejectReason] any Contains result of promise (returned by async function) rejection if function call was unsuccessful. undefined if asynchronous operation is running or promise was resolved.
params.execute ExecuteFunction Function for manual execution of asynchronous operation.

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


ResolvedChildrenFunction()ReactNode

Type of children function for Resolved

Params

Param Type
object
params.result any

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


RejectedChildrenFunction(params)ReactNode

Type of children function for Rejected

Params

Param Type
params object
params.rejectReason any

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


ExecutorChildrenFunction(params)ReactNode

Type of children function for Executor

Params

Param Type Description
params object
params.execute ExecuteFunction Function for manual execution of asynchronous operation.

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


StateChildrenFunction(params)ReactNode

Type of children function for State

Params

Param Type Description
params object
params.running boolean Whether async opertation is executing or not. If you only need to process running, use either Running or Completed component instead.
params.resolved boolean Whether async opertation was completed successfully last time or not. If you only need to process resolved and result, Resolved component instead.
params.rejected boolean Whether async opertation failed last time or not. If you only need to process rejected and rejectedStatus, use Rejected component instead.
[params.rejectReason] any Contains reject reason if async opertation failed last time. If you only need to process rejected and rejectedReason, use Rejected component instead.
[params.result] any Contains result of last successful async operation call. If you only need to process resolved and result, use Resolved component instead. If you need to accumulate result, consider ResultStore usage.
params.execute ExecuteFunction Callback for manual execution of async operation. If you only need to execute async operation manualy, use Executor component instead.

Returns: ReactNode - Should return rendered React component(s) depending on supplied params.


Change Log

You can find change log here

Credits

Great thanks to @kitos and @ventrz for their invaluable help, support and bright ideas!

react-async-call's People

Contributors

kuzn-ilya avatar semantic-release-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

ventrz kitos

react-async-call's Issues

react-async-call crashes with react >= 16.5.0

fbjs packages was eliminated from newest releases of react (version >= 16.5.0), so we need to grab invariant and shallowEqual from it or add fbjs as strict (not peer) dependency.

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


The push permission to the Git repository is required.

semantic-release cannot push the version tag to the branch master on remote Git repository with URL https://[secure]@github.com/kuzn-ilya/react-async-call.

Please refer to the authentication configuration documentation to configure the Git credentials on your CI environment and make sure the repositoryUrl is configured with a valid Git URL.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Add new PromiseRenderer.Runner component

It's usefull to have an ability to re-run promise-returning function without any changes in it's parameters. As for now there is no API for doing that.

Proposed API:

  • PromiseRenderer children function should receive one extra-argument of type () => void. Call of that function should lead to call of the promise-returning function.
  • PromiseRenderer should expose new component Runner. This new component should accept only function of type () => void as an oly child. Call of that function should re-execute promise-returning function.

Example:

  <PromiseRenderer>
    {(pending, result, rejected, rejectReason, run)=>(
      <button onClick={run}>Reload</button>
      pending && <div>Loading...</div>
      !pending && !rejected && <div>The result of function call is {result}</div>
      rejected && <div>Error: {rejectReason}</div>
    )}
  </PromiseRenderer>

... or ...

  <PromiseRenderer>
    <PromiseRenderer.Runner>
    {(run)=>(
      <button onClick={run}>Reload</button>
    )}
  </PromiseRenderer>

Production/Development builds

Now there is no bundle for production build, actually we have only development build.
Proposed change:

  • Build bundles via rollup api, not via rollup.config.js
  • Build two bundles: one for prod and another for dev.
  • Remove prop-types usage from prod build:
    • Add eslint rule react/forbid-foreign-prop-types
    • Add babel-plugin-transform-react-remove-prop-types into plugins
  • remove warning function usage from prod build.
  • to be checked remove invariant messages from prod build.

Tests Refactoring

Refactoring of tests has to be done before migrating to a new React Context API (#20). Tests depend on the inner structure of components, so we need to get rid of that dependencies, because migration to Context API will change tree of components' tree.

Add new property mergeResult into root component.

Proposed change

  1. Result of promise resolving should not be cleared between promise-returning function calls
  2. New property mergeResult: (prevResult: QueryResult, currentResult: QueryResult) => QueryResult should be added into root component

Re-work inner components structure and roles

At the moment there are several inner components supporting promise lifecycle. All of them implement conditional rendering in some scence:

  1. Running. Its children are rendered only during promise execution.
  2. Resolved. Its children are rendered when the promise resolved. But sub-component also renders it's children in some other tricky condition with the purpose to render accumulated data between promise-returning function calls.
  3. Rejected. Its children are rendered only after promise rejection (but before next succesful promise resolving).
  4. Executor. It passes execute method to its children function for running promise-returning function manually.

The main aims of creating sub-components above were:

  1. to have an more declarative way for handling promise lifecycle in comparison with "classic" render props approach, but also support the "classic" one.
  2. to keep sub-components API as simple as possible.

As @kitos mentioned, Rejected component renders its children during execution of promise and this is tricky, not clearly understandable and is subject to re-work sub-components structure.

If we state that promise can be only in three states (running, resolved and rejected) and a user should be able to identify them then we come to conclusion that 'Running', 'Resolved' and 'Rejected' components have to be in play. Only Resolved component should be changed: it should not render its children during promise run. It should render its children only between successful promise resolving and the next promise call.

But in this case we will not have a simple way (using sub-components) to render result of sequence of promise resolving (accumulating theirs results). Obviously it can be solved by introducing new sub-component (e.g. Result) which should render its children if promise was resolved at least once and should pass accumulated result of promise chain into its children function. So, this example will look like:

<PromiseRenderer params={this.state.since} mergeResult={(prevResult, currentResult) => [prevResult, ...currentResult]}>
  <table>
    // Table header render
    <tbody>
      <PromiseRenderer.Result>
        {result => result.map(item => (
          <tr key={item.id}>
            <td>{item.id}</td><td>{item.name}</td><td>{item.owner.login}</td>
          </tr>))}
      </PromiseRenderer.Result>
    </tbody>
  </table>
  <PromiseRenderer.Running>Loading...</PromiseRenderer.Running>
  <PromiseRenderer.Rejected>{status => <div>Error: {status.message}</div>}</PromiseRenderer.Rejected>
  <PromiseRenderer.Result>
    {result => <button  onClick={() => this.setState({ since: Math.max(...result.map(item => item.id)) })}>Next</button>}
  </PromiseRenderer.Result>
</PromiseRenderer>

To summarize:

  1. Resolved should render its children only between successful resolving and next promise run
  2. New component (e.g. Result) should render it's children if at least one promise-returning function was successful

@kitos, @ventrz?

AsyncCall context is not properly sent through AsyncCall.ResultStore

Children of AsyncCall.ResultStore does not receive proper context from root AsyncCall component, e.g.:

<AsyncCall params={this.state.since}>
  <AsyncCall.ResultStore>
     <AsyncCall.Executor>
        {({ execute }) => <button onClick={execute}>Reload</button>}
     </AsyncCall.Executor>
    </AsyncCall.ResultStore>
</AsyncCall>

This example throws the following warning into console:

Warning: Failed context type: The context `__react-async-call__2.execute` is marked as required in `AsyncCall.Executor`, but its value is `undefined`.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.