Saturday, September 15, 2018

[ Part 3 ] First Async call, redux-thunk


Previous Part (adding redux): http://dev.basharallabadi.com/2018/09/part-2-nutracker-reactjs-application.html

By default the store only understands object dispatched actions, i.e. it won't support async calls out of the box neither functions, which conflicts with our need to call an api asynchronously and get the results and returns those results to our reducers to update the state.


Redux Thunk

The simplest approach to achieve what we need is to use something called middleware in redux that can pre process our actions and then proceed to the store when it's done.
Redux thunk is a middleware library that knows how to handle a function received as a dispatched action from a component then based on the result it can dispatch more actions to the store to notify our reducers with the results.
mainly we use it to process async calls and based on the result (success/fail) we ask it to dispatch the proper actions when.

First lets update our foodRepo to actually call an api:
- install corss-fetch npm install --save cross-fetch to use the fetch API and get promises for our async calls.
- renamed the mock method in foodRepo to findFoodMock(), and created new one for the real call which does a fetch call to our api and parse the json body.
- I added this in the package.json: `proxy": "http://localhost:8080` to avoid Cross origin errors so react will proxy requests to this server through the development server.

findFood(name) {
return fetch(`/api/food/search?name=${name}`)
.then(response => response.json());
}

Actions Change

was:

export const SEARCH_FOOD = 'SEARCH_FOOD';
export function searchFood(term) {
 console.log(`in searchFood ${term}`);
 return {
  type: SEARCH_FOOD,
  term
 }
}
Let's change our actions to make an async call, we need two actions  :
- one to declare the call has started (so components can render loading spinner for example)
- another one to handle the result of the call
we already have SEARCH_FOOD action but now, it will now be used to:
- notify the reducers that the call started instead
to send the ajax call we will introduce a new function (fetchFood) that will:
- invoke the ajax call and host the logic to handle the promise result
- dispatch SEARCH_FOOD
the way we will handle the ajax call result is by dispatching a new action with the result as payload:
 RECEIVE_SEARCH_FOOD_RESULTS
after changes:

Reducers

modifed my reducers as well:
- if the action is SEARCH_FOOD it will set a flag that we are fetching data
- if the action is RECEIVE_SEARCH_FOOD_RESULTS, we will handle the results for that term, and update the state with the results array and unset the fetching flag.

what changes on the component side?

Previously in FoodSearch.js we dispatched the searchFood() action creator result (what builds SEARCH_FOOD action payload )
- the dispatch(searchFood(term)) will change now to dispatch(fetchFood(term))
- this new action creator returns a function that takes dispatch as parameter (to use it later on)

but as we said earlier: the store doesn't understand function actions by default and if you run the app now you will get this error message
Error: Actions must be plain objects. Use custom middleware for async actions.
so we will:
- install redux-thunk run npm install --save redux-thunk
- modify our store creation logic:
import thunkMiddleware from 'redux-thunk';
// lines omitted...
const store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
- running the code will show results from server.

Source for day 3: https://github.com/blabadi/react-nutracker/tree/day3/async-api-call
references: https://redux.js.org/advanced/asyncactions

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Istio —simple fast way to start

istio archeticture (source istio.io) I would like to share with you a sample repo to start and help you continue your jou...