Receipt App Redux Saga

ref – https://www.youtube.com/watch?v=4s2bTUbKNUs
https://stackoverflow.com/questions/71713111/mui-installation-doesnt-work-with-react-18

demo (search keyword and receives data)

Create the app.

npx create-react-app recipe-app-redux

cd receipe-app-redux

Install packages required to build this app.

npm i react-redux redux redux-saga redux-logger

npm i axios
npm install @mui/material @emotion/react @emotion/styled –legacy-peer-deps
npm install @mui/icons-material –legacy-peer-deps

First, we create a saga middleware.
then we insert the middleware into the store.
Finally, we run imported root saga in our saga middleware.

src/redux/store.js

First know that whenever we dispatch an action for type FETCH_RECIPE_START, we will execute onLoadRecipeAsync.
takeLatest listens for action types (i.e FETCH_RECIPE_START), and then execute the corresponding function (i.e onLoadRecipeAsync)

(note: for takelatest, if task A was started previously and still running, we cancel the current one)

Hence takelatest is like registering for event listeners. Thus we need to execute this ‘registration’ via fork. Fork is non-blocking because
we want to asynchronously do this registration.

We then use all to make sure all “async registrations” (i.e recipeSaga) are run in parallel and wait for all of them to complete.

fork vs call

First, some concepts.

ref –

  • https://stackoverflow.com/questions/47798900/redux-saga-call-vs-fork-and-join
  • https://stackoverflow.com/questions/42938520/redux-saga-when-to-use-fork/42961360#42961360

src/redux/sagas.js

so essentially, what we’re doing here is to make sure we register all takeLatest, which registers event listeners for action type to functions.

Now, let’s look at the worker function onLoadRecipeAsync. What we’re trying to do here is to run a getRecipes function with some query data. getRecipes just makes a request to a server somewhere. THEN, we put the response data into the reducer. These two steps must be synced. Step 1 must finish first, then step 2 can go.

Hence that’s why we use call. getRecipes itself is a Promise, so it runs asynchronously, but we use call to pause saga so that the next saga effect will wait for the response of this saga effect.

src/redux/sagas.js

After receiving the response, we can then use put to put the data into the reducer. put is non-blocking. (We have received the data already…storing data can be done asynchronously here)

Fetching the Data

The word non-blocking is usually used with I/O, while asynchronous is used in a more generic sense with a broader range of operations.

But few major differences are:1) Asynchronous does not respond immediately, While Nonblocking responds immediately if the data is available and if not that simply returns an error.2) Asynchronous improves the efficiency by doing the task fast as the response might come later, meanwhile, can do complete other tasks.

Fetching the data is simply a Promise. It asynchronously does an I/O operation for fetch in the web environment using axios. Thus, this is non-blocking.

src/redux/api.js

Notice that we use call to execute getRecipes which returns a promise. call is blocking, while getRecipes is async. So is this all blocked or simply all async? The answer is that due to getRecipes being an I/O operation for the web environment, it is non-blocking. So UI still functions. The blocking part from call pauses the saga middleware so that future middleware must wait for its data response.

Implementing the UI

todo – use Antd for UI since now we have all the data.