Consume APIs with react/redux without the pain of writing actions and reducers as simple as:
const [login, loginState] = useApiAction(Api, api => api.authentication.login)
- Redux
- Multiple APIs support
- Typescript support
- React Hooks and Class components
npm install --save use-api-action
yarn add use-api-action
An API definition should have groups. Within each group, define the endpoints as shown below. Axios is required for this, as the underlying functionality depends on the AxiosResponse object.
You can use Typescript to define the argument types as you wish.
const testApi = {
authentication: {
login: (username, password) => axios.post('/login', { username, password }),
register: (username, password) => axios.post('/register', { username, password })
},
users: {
list: () => axios.get('/users')
},
products: {
getProduct: productId => axios.get('/products/' + productId)
}
}
Provide the API definition and a name for the API. This name should be unique when defining multiple APIs.
const Api = buildApiReducer(testApi, 'Test')
const Api2 = buildApiReducer(productsApi, 'Products')
Combine your APis into one reducer.
const apis = combineApiReducers({ Api, Api2 })
Register the APIs to your store.
const rootReducer = combineReducers({
//... your other reducers here. You have to pass apiReducer under 'apis' key
apis: apis.reducer
})
// Ensure you have registered apis.middleware as well.
const store = createStore(rootReducer, applyMiddleware(apis.middleware))
You can either use the hook or use Higher Order Components.
const LoginPage = props => {
const [login, loginState] = useApiAction(Api, api => api.authentication.login)
return <div></div>
}
class LoginPage extends React.Component {
render() {
const [login, loginState] = this.props.login
}
}
export default connectApi(Api, api => {
return {
login: api.authentication.login // Maps "login" to the props.
}
})(LoginPage)
class LoginPage extends React.Component {
render() {
const [login, loginState] = this.props.login()
const onSubmit = (username, password) => {
// Invoking this will automatically pass the arguments to the api definition endpoint.
login(username, password)
}
return loginState.submitting ? <div>Logging in...</div> : <LoginForm onSubmit={onSubmit} />
}
}
The api action returns an array with the same structure as the useState
react hook. With the following structure.
const [login, loginState, clearLogin] = useApiAction(Api, api => api.authentication.login)
// For class components
const [login, loginState, clearLogin] = props.state.login()
-
The first element is the
actionCreator
. It is used to make the api call specified. In this example, you will invoke it with theusername
andpassword
arguments.login('username', 'password')
It returns the following.
{ payload: [], onSuccess: (response, payload) => {} onError: (error, payload) => {} statusCode: 500 id: "default" // The id can be used to distinguish two separate states to the same API cal clearData: boolean // clear the existing data when the api call made. clearErrors: boolean // clear the existing errors when the api call is made. errorHandler: (error) => {}, onNewData?: (prevState, newState) => State }
-
The second argument is the
state
of the action. It is defined as follows:{ "data": null, "errors": null, "failed": false, "submitting": false, "submitted": false, "fetching": false, "fetched": false, "statusCode": 200 }
Field Description data The response data returned by your API after a success errors The response errors returned by your API after an error failed The failure status. It is true if there was an error when fetching data submitting Set to true when the data is being fetched submitted Set to true when the data has been fetched whether there was an error or not statusCode Updated after every successful / failed request fetched Set to true when the data has been fetched only when there was no error. fetching Similar to submitting, to show when data is being fetched. -
The third argument is the
clear
action. It is used to clear the state(data, errors, statusCode etc.)
and restore it to the initial state.useEffect(() => { const { id } = login('username', 'password') return () => clearLogin(id) }, [])
You can pass response handlers for both onSuccess
and onError
to the api action.
// hooks
const [login, loginState] = useApiAction(Api, api => api.authentication.login, {
onSuccess: (response, payload) => {
// do something with the response / payload
},
onError: (error, payload) => {}
})
// class components
const [login, loginState] = this.props.login({
onSuccess: (response, payload) => {},
onError: (error, payload) => {}
})
You can clear data before making a new api call. By default, the existing data is kept until the api request is successful.
// hooks
const [login, loginState] = useApiAction(Api, api => api.authentication.login, {
clearData: true
})
// class components
const [login, loginState] = this.props.login({ clearData: true })
You can clear the existing errors before making a new api call. By default the existing errors are kept until there are new errors.
// hooks
const [login, loginState] = useApiAction(Api, api => api.authentication.login, {
clearErrors: true
})
// class components
const [login, loginState] = this.props.login({ clearErrors: true })
You can modify the existing state after making a new request
// hooks
const [getStudents, studentList] = useApiAction(Api, api => api.students.list, {
onNewData: (prevStudents, students) => ([...prevStudents, ...students])
})
const [getStudents, studentList] = this.props.students({
onNewData: (prevStudents, students) => ([...prevStudents, ...students])
})
Copyright © Moses Gitau All rights reserved.
Find a copy of the License here