diff --git a/README.md b/README.md index 2781e0c..49f34ab 100644 --- a/README.md +++ b/README.md @@ -36,26 +36,24 @@ $ npm install react-loads ```js import React from 'react'; -import Loads, { IfIdle, IfLoading, IfTimeout, IfSuccess, IfError } from 'react-loads'; +import Loads from 'react-loads'; const getRandomDog = () => axios.get('https://dog.ceo/api/breeds/image/random'); export default () => ( - {({ load, response, error }) => ( + {({ isIdle, isLoading, isSuccess, load, response, state, error }) => (
- - - - loading... - taking a while... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error} + )}
)}
@@ -76,7 +74,7 @@ export default () => ( - + @@ -92,60 +90,15 @@ export default () => ( + + + + + -
PropTypeDefault valueDescription
children ({ response?: any, error?: any, load: (...args: any) => ?Promise<any>, reset: Function, state: 'idle' | 'loading' | 'timeout' | 'success' | 'error' })N/A (required)
children ({ response?: any, error?: any, load: (...args: any) => ?Promise<any>, resetState: Function })N/A (required)
delay number300 Number of milliseconds before component transitions to loading state upon invoking fn/load.
loadOnMount booleanfalse Whether or not to invoke the fn on mount.
fn (...args: any) => Promise<any>N/A (required) The promise to invoke.
response anyResponse from the resolved promise (`fn`)
error anyError from the rejected promise (`fn`)
load (...args: any) => ?Promise<any>Trigger to load `fn`
isIdle booleanReturns `true` if the state is idle (`fn` has not been triggered).
isLoading booleanReturns `true` if the state is loading (`fn` is in a pending state).
isTimeout booleanReturns `true` if the state is timeout (`fn` is in a pending state for longer than `delay` milliseconds).
isSuccess booleanReturns `true` if the state is success (`fn` has been resolved).
isError booleanReturns `true` if the state is error (`fn` has been rejected).
resetState () => voidReset state back to `idle`.
state 'idle' | 'loading' | 'timeout' | 'success' | 'error'Current state.
-### ``, ``, ``, ``, `` - -These components determine what node to render based on the loading/response state. - -#### Definitions - -- `IfIdle` - Will render when the state is 'idle' (the initial state). -- `IfLoading` - Will render when the state is 'loading' (triggered when the promise (`fn`) is pending). -- `IfTimeout` - Will render when the state is 'timeout' (triggered when the promise (`fn`) has not resolved/rejected after a period of time). -- `IfSuccess` - Will render when the state is 'success' (triggered when the promise (`fn`) resolves). -- `IfError` - Will render when the state is 'error' (triggered when the promise (`fn`) rejects). - -#### Props - - - - - - - - - -
PropTypeDefault valueDescription
channelstringnull The key of the context from where to read the state.
childrennodeN/A (required) The children to be rendered when the - conditions match.
onShowfunc The function invoked when the component becomes visible.
onHidefunc The function invoked when the component becomes hidden.
- -### `` - -A component to define which parts of the tree should be rendered for a set of states. - -#### Props - - - - - - - - - - -
PropTypeDefault valueDescription
isoneOfType(arrayOf(string), string)N/A (required) The states(s) for which the children should be shown. Available states: 'idle', 'loading', 'timeout', 'success', 'error'
channelstringnull The key of the context from where to read the state.
childrennodeN/A (required) The children to be rendered when the - conditions match.
onShowfunc The function invoked when the component becomes visible.
onHidefunc The function invoked when the component becomes hidden.
- -```jsx - - Hello world! - -``` - ## Special thanks - [Michele Bertoli](https://github.com/MicheleBertoli) for creating [React Automata](https://github.com/MicheleBertoli/react-automata) - it's awesome and you should check it out. diff --git a/package.json b/package.json index b595a36..2838b3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-loads", - "version": "3.1.0", + "version": "4.0.0-canary.1", "description": "A simple React component to handle loading state", "license": "MIT", "repository": "jxom/react-loads", diff --git a/src/__stories__/index.stories.js b/src/__stories__/index.stories.js index 3036c39..49f4ba8 100644 --- a/src/__stories__/index.stories.js +++ b/src/__stories__/index.stories.js @@ -3,27 +3,26 @@ import React, { Fragment } from 'react'; import { storiesOf } from '@storybook/react'; import axios from 'axios'; -import Loads, { IfState, IfIdle, IfLoading, IfSuccess, IfTimeout, IfError } from '../index'; +import Loads from '../index'; storiesOf('Loads', module) .add('default usage', () => { const getRandomDog = () => axios.get('https://dog.ceo/api/breeds/image/random'); return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, load, response, state, error }) => (

Current state: {state}

- - - - loading... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error} + )}
)}
@@ -33,20 +32,19 @@ storiesOf('Loads', module) const getRandomDog = () => axios.get('https://dog.ceo/api/breeds/image/random'); return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, load, response, state, error }) => (

Current state: {state}

- - - - loading... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error} + )}
)}
@@ -56,20 +54,19 @@ storiesOf('Loads', module) const getRandomDog = () => axios.get('https://dog.ceo/api/breeds/image/random'); return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, load, response, state, error }) => (

Current state: {state}

- - - - loading... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error} + )}
)}
@@ -79,20 +76,19 @@ storiesOf('Loads', module) const getRandomDog = () => axios.get('https://dog.ceo/api/breeds/image/random'); return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, load, response, state, error }) => (

Current state: {state}

- - - - loading... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error} + )}
)}
@@ -104,20 +100,20 @@ storiesOf('Loads', module) }; return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, isError, load, response, state, error }) => (

Current state: {state}

- - - - loading... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error && error.message} + )} + {isError &&
Error! {error}
}
)}
@@ -128,21 +124,20 @@ storiesOf('Loads', module) new Promise(resolve => setTimeout(() => resolve(axios.get('https://dog.ceo/api/breeds/image/random')), 3000)); return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, isTimeout, load, response, state, error }) => (

Current state: {state}

- - - - loading... - taking a while... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isTimeout &&
taking a while...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error && error.message} + )}
)}
@@ -152,20 +147,19 @@ storiesOf('Loads', module) const getRandomDogByBreed = breed => axios.get(`https://dog.ceo/api/breed/${breed}/images/random`); return ( - {({ load, response, state, error }) => ( + {({ isIdle, isLoading, isSuccess, load, response, state, error }) => (

Current state: {state}

- - - - loading... - - {response && Dog} + {isIdle && } + {isLoading &&
loading...
} + {isSuccess && (
- + {response && Dog} +
+ +
-
- Error! {error} + )}
)}
@@ -175,26 +169,40 @@ storiesOf('Loads', module) const getRandomDog = () => axios.get(`https://dog.ceo/api/breeds/image/random`); const saveDog = randomDogResponse => new Promise(resolve => setTimeout(() => resolve(randomDogResponse), 1000)); return ( - - {({ load: loadRandomDog, response: randomDogResponse, error: randomDogError }) => ( - - {({ load: saveDog, state: saveDogState }) => ( + + {({ + isIdle: isRandomDogIdle, + isLoading: isRandomDogLoading, + isError: isRandomDogError, + isSuccess: isRandomDogSuccess, + load: loadRandomDog, + response: randomDogResponse, + error: randomDogError + }) => ( + + {({ + isLoading: isSaveDogLoading, + isSuccess: isSaveDogSuccess, + isIdle: isSaveDogIdle, + load: saveDog, + state: saveDogState + }) => (
- - - - loading... - - {randomDogResponse && Dog} + {isRandomDogIdle && } + {isRandomDogLoading &&
loading...
} + {isRandomDogSuccess && (
- saving... - saved dog! - - - + {randomDogResponse && Dog} +
+ {isSaveDogLoading &&
saving...
} + {isSaveDogSuccess &&
saved dog!
} + {(isSaveDogSuccess || isSaveDogIdle) && ( + + )} +
-
- Error! {randomDogError} + )} + {isRandomDogError &&
Error! {randomDogError}
}
)}
diff --git a/src/index.js b/src/index.js index 1e53870..7ced9f1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,7 @@ // @flow -import { createElement, Component } from 'react'; +import { Component } from 'react'; import { withStatechart } from 'react-automata'; -export * from './states'; const statechart = { initial: 'idle', @@ -49,10 +48,15 @@ const statechart = { type Props = { children: ({ - response?: any, - error?: any, - load?: (...args: any) => ?Promise, + error: any, + isError: boolean, + isIdle: boolean, + isLoading: boolean, + isSuccess: boolean, + isTimeout: boolean, + load: (...args: any) => ?Promise, resetState: () => void, + response: any, state: string }) => any, delay?: number, @@ -157,6 +161,11 @@ class Loads extends Component { const state = machineState.value; return children({ error, + isIdle: state === 'idle', + isLoading: state === 'loading', + isTimeout: state === 'timeout', + isSuccess: state === 'success', + isError: state === 'error', load: this.handleLoad, resetState: () => this.transition('RESET'), response, @@ -165,9 +174,4 @@ class Loads extends Component { }; } -const _default = ({ channel, ...props }: { channel?: ?string }) => - createElement(withStatechart(statechart, { channel })(Loads), props); - -_default.defaultProps = { channel: null }; - -export default _default; +export default withStatechart(statechart)(Loads); diff --git a/src/states.js b/src/states.js deleted file mode 100644 index 9088c53..0000000 --- a/src/states.js +++ /dev/null @@ -1,20 +0,0 @@ -// @flow -import React, { type Node } from 'react'; -import { State as AutomataState } from 'react-automata'; - -type State = 'success' | 'error' | 'timeout' | 'loading' | 'idle'; -type StateProps = { - channel?: string, - children: Node -}; - -export const IfState = ({ channel, children, is, ...props }: { ...StateProps, is: Array | State }) => ( - - {children} - -); -export const IfIdle = (props: StateProps) => IfState({ is: 'idle', ...props }); -export const IfLoading = (props: StateProps) => IfState({ is: 'loading', ...props }); -export const IfTimeout = (props: StateProps) => IfState({ is: 'timeout', ...props }); -export const IfSuccess = (props: StateProps) => IfState({ is: 'success', ...props }); -export const IfError = (props: StateProps) => IfState({ is: 'error', ...props });