0.9.4 โ€ข Published 21 days ago

react-async-stateful v0.9.4

Weekly downloads
307
License
MIT
Repository
github
Last release
21 days ago

react-async-stateful ยท GitHub license npm version

A consistent method of storing state for async calls to reduce boilerplate in react code

npm i react-async-stateful

Usage

// plain js
import {useAsyncState} from "react-async-stateful";

const AsyncComponent = (props) => {
    const [submitResult, _, updateSubmitResult] = useAsyncState();    

    const submit = useCallback(() => {
        const {resolvedAt} = updateSubmitResult(async () => {
            const response = await fetch("https://example.com/api/v1/squeal-loudly");
            return response.json();
        });
        console.log(`API responded at: ${new Date(resolvedAt).toString()}`)
    });
    
    return <div>
        <button onClick={submit}>Call the API!</button>
        {submitResult.resolved && <div>The response: {JSON.stringify(submitResult.value)}</div>}
    </div>;
};

Demos

useAsyncState hook

The useAsyncState hook returns:

    const [asyncState, setAsyncState, updateAsyncState] = useAsyncState(defaultValue);
  • asyncState is the the current value of the async state
  • setAsyncState is usually never needed but can be useful to synchronously update the state, eg:

    import {resolve} from "react-async-stateful";
    
    const updateFromLocalStorage = () => {
      setAsyncState(asyncState => {
          const value = localStorage.getItem("key");
          return resolve(asyncState, value);
      });
    };
  • updateAsyncState is the recommended way of updating. It will automatically update the state and re-render your component with a pending state allowing you to dipslay loading spinners ect.

    const submit = useCallback(() => {
      updateAsyncState(/*promise or an async function*/ async () => {
          const response = await api.get(`user/${userId}`); // thrown errors are automatically handled
          const user = formatUserObject(response.data);
          return user;
      });
    }, [userId]);
    
    // component will receive:
    asyncState.pending === true
    
    // then
    asyncState.resolved === true
    asyncState.value    === user // resolved value
    
    // or if a rejection
    asyncState.rejected === true
    asyncState.error    === Error // error instance

Submit vs Refresh

The default behaviour is that when the updateAsyncState function is called, the current value and errors are wiped and the state is put into an empty pending state.

This can be undesirable if you are merely refreshing data and want to keep the previous value whilst the new request is being made. To fix this you can pass the refresh option so that these are kept:

const refreshList = () => {
    updateList(async () => {
        const response = await api.get("list");
        return response.data;
    }, {refresh: true} /* <-- keep the current value whilst we are pending */);
};

whether submit or refresh were used is stored as submitType on the async state object

The AsyncState object

Note: All operations on async state do not mutate the original object

// typescript
import * as AsyncState from "react-async-stateful";

// creating the state
const state = AsyncState.create("hello");
console.log(state.pristine); // true
console.log(state.value); // hello

// resolving the state
const resolvedState = AsyncState.resolve(state, "world");
console.log(state.resolved); // true
console.log(state.value); // world

Properties

keytypedescription
defaultValueT or undefineddefault value set at the object creation
pristinebooleanhave we been updated yet?
pendingbooleanis there an update to the state happening now?
pendingAtnumber or nullwhen the update started
resolvedbooleando we have a resolved value? if this is true then value must not be undefined
resolvedAtnumber or nullwhen the value the was resolved
rejectedbooleanif an error occurred or the update was rejected. if this is true then error must not be undefined
rejectedAtnumber or nullwhen the rejection happened ๐Ÿ˜ข
settledbooleanif the object is resolved/rejected and not pending
settledAtnumber or nullwhen it was settled
valueT or undefinedthe currently resolved value, if undefined we are not resolved
errorError or undefinedthe reason for the rejection, if undefined we are not rejected
submitTypeAsyncStateSubmitType or undefinedwhat kind of submit was it? can be either submit or refresh

Typescript Utils

Typescript has a cool feature allowing you to narrow the type of an object using methods. Supplied are some methods that will make null checking your async state objects easier:

import {isResolved, useAsyncstate} from "react-async-stateful";

const [asyncState, _, updateAsyncState] = useAsyncstate<UserData>();

runUpdateThatWillHappenInTheFuture();

/* Typescript will complain about the line below ๐Ÿ˜ซ 
   Even though our library provides a contract that if resolved is true
   `value` cannot be undefined the typescript compiler has no way of knowing this! */
// return asyncState.resolved ? <div>{asyncState.value.id}</div> : "Loading";

// Use `isResolved` and the compiler will be happy that it is definitely present:
return isResolved(asyncState) ? <div>{asyncState.value.id}</div> : "Loading";

To-Do

  • tests
  • better docs
  • redux actions + reducers
0.10.0-alpha.7

21 days ago

0.10.0-alpha.3

21 days ago

0.10.0-alpha.2

21 days ago

0.10.0-alpha.5

21 days ago

0.10.0-alpha.4

21 days ago

0.10.0-alpha.1

21 days ago

0.9.4

2 years ago

0.9.0

2 years ago

0.9.2

2 years ago

0.9.1

2 years ago

0.9.3

2 years ago

0.9.0-alpha13

3 years ago

0.9.0-alpha11

3 years ago

0.9.0-alpha12

3 years ago

0.9.0-alpha10

3 years ago

0.9.0-alpha9

3 years ago

0.9.0-alpha8

3 years ago

0.9.0-alpha7

3 years ago

0.9.0-alpha6

3 years ago

0.9.0-alpha5

3 years ago

0.9.0-alpha4

3 years ago

0.9.0-alpha3

3 years ago

0.9.0-alpha2

3 years ago

0.9.0-alpha1

3 years ago

0.8.2

4 years ago

0.8.1

4 years ago

0.8.0

4 years ago

0.7.0-alpha3

4 years ago

0.7.0-alpha1

4 years ago

0.7.0-alpha2

4 years ago

0.7.0

4 years ago

0.6.1

4 years ago

0.6.0

4 years ago

0.5.6

4 years ago

0.5.5

4 years ago

0.5.4

4 years ago

0.5.3

4 years ago

0.5.2

4 years ago

0.5.1

4 years ago

0.5.0

4 years ago

0.5.0-alpha1

4 years ago

0.4.0

4 years ago

0.3.2

4 years ago

0.3.1

4 years ago

0.3.0

4 years ago

0.3.0-alpha8

4 years ago

0.3.0-alpha6

4 years ago

0.3.0-alpha7

4 years ago

0.3.0-alpha5

4 years ago

0.3.0-alpha4

4 years ago

0.3.0-alpha2

4 years ago

0.3.0-alpha3

4 years ago

0.3.0-alpha1

4 years ago

0.2.0

4 years ago

0.1.2

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago

0.0.1

4 years ago