Category Archives: Uncategorized

createAsyncThunk – Redux ToolKit (RTK)

ref – https://blog.logrocket.com/using-redux-toolkits-createasyncthunk/

Redux store possesses great state management features, but it doesn’t know how to deal with asynchronous logic.

Redux can’t handle asynchronous logic simply because it doesn’t know what you want to do with the data you fetched.

Middleware has since been used in Redux applications to perform asynchronous tasks.
Redux Thunk’s middleware being the most popular package.

With Redux Toolkit,
Redux Thunk is included by default.
This allows createAsyncThunk to perform delayed, asynchronous logic before sending the processed result to the reducers.

The Slice

We create the Redux slice via createSlice function.
A slice is a function that contains your store and reducer functions used to modify store data
Its name is posts, and we initialize it with some default data.

As you can see we create a getPosts that uses createAsyncThunk function. We initialize its action type string as ‘posts/getPosts’.
This is used in reducers in switch statements to catch the data to be stored.

Then we implement the async functionality.

We do our async feature in there and then return the data.

  • Synchronous Requests – Within createSlice, synchronous requests made to the store are handled in the reducers object
  • Async Requests – Within createSlice, extraReducers handles asynchronous requests, which is our main focus

Asynchronous requests created with createAsyncThunk accept three parameters:

  1. an action type string
  2. a callback function (referred to as a payloadCreator)
  3. and an options object

ref – https://redux-toolkit.js.org/api/createAsyncThunk#payloadcreator

It’s important to note that payloadCreator accepts only two parameters:

  • custom argument that may be used in your request
  • thunkAPI is an object containing all of the parameters that are normally passed to a Redux Thunk function — like dispatch and getState. Take a look at all the acceptable parameters.

    The payloadCreator function will be called with two arguments:

    arg: a single value, containing the first parameter that was passed to the thunk action creator when it was dispatched. This is useful for passing in values like item IDs that may be needed as part of the request. If you need to pass in multiple values, pass them together in an object when you dispatch the thunk, like

    thunkAPI: an object containing all of the parameters that are normally passed to a Redux thunk function, as well as additional other options:

    • dispatch: the Redux store dispatch method
    • getState: the Redux store getState method

    For example, in this payloadCreator, we have the main object goalData, which has all the data packaged into an object, so that it can contain various properties and their values.

    Then we have thunkAPI, which has getState, and dispatch. In our example, it uses the getState, and then uses the auth slice to access its user’s token value:

    Whenever the payloadCreator is dispatched from a component within our application, createAsyncThunk generates promise lifecycle action types using this string as a prefix:

    pending: posts/getPosts/pending
    fulfilled: posts/getPosts/fulfilled
    rejected: posts/getPosts/rejected

    The three lifecycle action types mentioned earlier can then be evaluated in extraReducers, where we make our desired changes to the store. In this case, let’s populate entities with some data and appropriately set the loading state in each action type:

    Dispatching it in your UI

    By using useSelector and useDispatch from react-redux, we can read state from a Redux store and dispatch any action from a component, respectively.

    Example

    Given a fetch function in a service file.
    We have a function that takes in data, and a token.
    This function will be used in a payloadCreator.

    frontend/src/features/goals/goalService.js

    Thus, we receive this data and token from thunkAPI:

    frontend/src/features/goals/goalSlice.js

    1) So we import an action function from our slice.
    2) We then dispatch that action.

Create DB (Rest API)

ref – https://www.youtube.com/watch?v=-0exw-9YJBo

console.log(your string.cyan.underline);

Create folder

mkdir mern-tutorial
cd mern-tutorial
Code .

empty folder

create folder backend

in backend folder, create file server.js

npm init

entry point: (index.js) server.js

create another file .gitignore:

npm i express dotenv mongoose colors

npm i -D nodemon

package.json

Add git repository

git init
git add .
git commit -m “Initial Commit”

server.js

create .env

.env

Now we can do:

Create routes folder in backend folder.
Then create goalRoutes.js in routes folder.

goalRoutes.js

create controllers folder.

create goalController.js in controllers folder.

Create MongoDB

mongodb+srv://admin:admin@cluster0.z0xxwz0.mongodb.net/mernapp

.env

questionnaire env

questionnaire-node

ATLAS_URI=”mongodb+srv://admin:admin@cluster0.z0xxwz0.mongodb.net/?retryWrites=true&w=majority”
PORT=”8800″
SECRET_KEY = rickyabc

questionnaire react

REACT_APP_SERVER_HOSTNAME=’https://api.rickyabc.com’
REACT_APP_API_URL = ‘https://api.rickyabc.com/api’
PORT=3000
PUBLIC_URL=https://rickyabcenglish.com

Axios and fetch both returns a Promise object

ref –

  • https://stackoverflow.com/questions/37555031/why-does-json-return-a-promise
  • https://stackoverflow.com/questions/67043652/why-is-response-json-returning-a-promise

Fetch will return a Promise object rsp.
However, when you access json() of rps, it also returns a promise.

Why is this?

Because you receive the response as soon as all headers have arrived.

Calling .json() gets you another promise for the body of the http response that is yet to be loaded.

So when the headers have arrived, the promise for the fetch will resolve. But in order to the data from the body, you’ll have to wait for the promise from json() to resolve.

Another way to do it is to receive the promise object from then callback.
It is a Promise, so we’ll have to wrap it with async.

This promise means the header has arrived. Then in order to receive the body data, we’ll have to do an await on the promise object’s json(), which returns another Promise.

Once this Promise resolves, it means the body data has arrived in data.

Quiz App

ref – https://www.youtube.com/watch?v=BNN4o4gnSF4

How to Operate

If you need to make updates to the questions and answer, make sure you delete existing object in questions collections.

Then create a new object with Postman like so:

POST localhost:8080/api/questions

This way, it updates mongo db.

When there are already data in results and you want to clear it, in your Postman:

DELETE localhost:8080/api/result

In our results reducer, we keep state for wechat id, douyin id and the user’s results. All answers from this user will will be saved in this slice.

Users and checking for existing users

First we check to see if the user’s wechat id or telephone exists.

When we start the app, we first save the user’s id from input to slice:

src/components/Main.js

We do a fetch using checkIfUserExistInMongoDB. It will return the user’s information if EITHER the wechat id or mobile matches. If it does, we throw out an error message.

If BOTH wechat id and mobile does not exist, we can proceed to save the data locally to the store:

src/components/Main.js

In startQuiz function, we dispatch actions generated from result_reducer.

src/redux/question_reducer.js

we throw in the user entered string into the action.
src/components/Main.js

The action object gets into the reducer.

src/redux/result_reducer.js

Question Reducer

Question reducer keeps track of what question index we’re on, the questions themselves, and the answers.

state:
queue is to represent the array of questions.
answers is to represent the array of answers the user inputs
questionIndex is to represent the cur of the data structure.

src/redux/question_reducer.js

When the next button is clicked, we dispatch an action to move the question index forward.
When the prev button is clicked, we dispatch an action to move backwards.

This is very similar to how a custom array would work. We have the array data, but keep a cur to keep track of what index we’re on.

src/hooks/FetchQuestion.js

Okay, so this is all happening in the store.

But for the UI, before we even touch the store, we need to create local state in order to get the user selected radio index. So we define value for the radio button index that the user has checked:

src/components/Quiz.js

We then get the current question by getting the questionIndex and using it to get the element in queue.

Now we render the options associated with this question object.

As you know aQuestion is an object from the store.
It contains:

  • question
  • id – the id of the whole question
  • options – an array of answer values. Thus, we use map to iterate through all the answer available.

src/components/Questions.js

Fetching that Question object

So we see how we retrieve the question object from the store. But how did we fetch from the web?

We use a custom hook called useFetchQestion.

src/components/Questions.js

It runs whenever our Question component is refreshed.

We start off basics. We use local state to show:

  • isLoading – whether something is loading/done
  • apiData – data container from the response object
  • serverError – will have data when/if error occurs

We initialize local data to default. As long as the component does not update, we use the same dispatch reference.

Note:

Its safe to add dispatch to the dependency array because its identity is stable across renders.

The doc says like in React’s useReducer, the returned dispatch function identity is stable and won’t change on re-renders
unless you change the store being passed to the , which would be extremely unusual

In similar situation, the dispatch function reference will be stable as long as the same store instance is being passed to the . Normally, that store instance never changes in an application.

However, the React hooks lint rules do not know that dispatch should be stable, and will warn that the dispatch variable should be added to dependency arrays for useEffect and useCallback. The simplest solution is to do just that:

Using and declaring async IIFE function in the effect to fetch data

In useEffect, we must give the declaration and immediately invoke it in order for the async operation to be part of the effect. Thus that’s why we declare the function, and then execute it.

If there is an error, we set local data isLoading to false so any kind of spinner can stop.
We also pass the error object to serverError.

Successfully fetching the data

Where getServerData awaits on an axios’s get on url http://localhost:8080/api/questions

src/helper/helper.js

ref – http://chineseruleof8.com/code/index.php/2022/12/16/axios-and-fetch-both-returns-a-promise-object/

So we see two awaits. First, we return a promise when the headers arrive. This is to let us know that the get response has returned. But in order to the get json data response, we’ll have to call json() to get another Promise object, which resolves to our data. Hence this is why we need a 2nd await.

After data returns, we create an array holder for it.

If questions array is valid, we first set local isLoading to false to say we’re done fetching data.

Then, we apply questions to local state apiData. Once we have have apiData, getData will have the valid data to be used and we return it from the functional component. However, in our app we don’t use this apiData data. We would fetch from the store as will be explained later.

src/components/Questions.js

After we dispatch action startExamAction, it goes to our question reducer startExamAction and the data is loaded from the action object’s payload.
We then assign them to the slice’s state:

src/redux/question_reducer.js

Pushing Next button

When the next button is pushed, it will evaluate what question index we’re on from the state.questions via question index.

If it’s still within the index of the last question, we will update the state to the next question.

src/components/Quiz.js

dispatch(MoveNextQuestion()) will increase the questionIndex:

src/redux/question_reducer.js

Once questionIndex is updated, then when we retrieve a question object from the store, it will update:

src/components/Questions.js

We know that questionIndex gets updated from pressing Next button, how does it get rendered?

How does Question data get rendered?

Keeping track of the question index

questionIndex is increased/decreased via MoveNextQuestion.

Redux-Saga with Redux-Toolkit | Movie Search App

ref – https://www.youtube.com/watch?v=DPOzlL1fpnU

npx create-react-app movie-app

npm install @mui/material @emotion/react @emotion/styled

npm install @mui/material react-redux @reduxjs/toolkit redux-saga react-router-dom axios

First, we have a simple Promise that fetches data from the server.
This is a non-blocking operation, because it involves I/O with the web environment.
Thus when we asynchronously execute the fetch, we can let it process and go on to other tasks.

src/redux/api.js

Since we asynchronously execute the fetch, we use call. This is because call stops the saga middleware and waits for its operation to finish.
Once we get the response, then we continue on. fetchMovies still processes it asynchronously.

Finally, we use put (non-blocking) to put the data in the store. (We have received the data already, so storing data can be done asynchronously here)

src/redux/movieSagas.js

and executes onLoadMoviesAsync when it receives that message.

Notice that takeLatest listens for getMovies.type, which logs ‘movie/getMovies’. But how did it create this tring?

It takes the name property of the slice:

and then append the action name “getMovies” behind it.

src/redux/feature/movieSlice.js

If you log getMovies, which is a function reference, you’ll see that it creates an action for you:

type is passed in from createAction from another execution so we know that we’re being used as a curried function.

Run the app and we start with initial state:

Then we do a get movies, which returns the initial state:

We run our dispatch getMovies in useEffect of our Search component, which has default value ‘spider’ and get a return of all movies with spider keyword.

movie-app demo part 1

– 38:00 at the video

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.

Refresh Tokens and reuse detection

demo download

Functionalities

Simple log in, log out

First make sure there are no previous cookies in your browser.

Simply log in, and make sure we receive a refresh token.

When we log out, make sure our refresh token array is emptied.

Visit /login with previous refresh token in cookie

First, we have a token (expired or not is ok) in our cookie.

When we look at the cookie on the server side, make sure the jwt token is there.

So whenever we hit /login url, it will execute handleLogin on the server.

We find the user from User Collections, then make sure the passwords match via bcrypt.compare
This tells us if we have a valid user.

If we do, we simply create a new access token and refresh token combo. This is always true, when the user visits /login

Now, the question is, what do we do if we already have a previous refresh token? (embedded in the cookie)

The answer is, we check to see if the previous token exists in the user’s refresh token array. We KEEP any refresh token that does NOT match the previous refresh token.

This is because we don’t want/care about the previous refresh token when we visit /login
We filter it out!

If the user has logged in AND there is a jwt embedded in the cookie, we simply clear the cookie. This way, we force the newly logged in user to use the latest created refresh token.

The next step is that we go the normal route. We ADD newly created refresh token to the user’s refresh token array.

Using expired token

* make sure your refresh token is set to 60 seconds.

right before you sign in, there may be expired tokens from previous usage.

Log in (/login) you’ll get an access and refresh token.

Click on the admin link. Look at the refresh token.
In Cookies, click on jwt, and copy the token.

Then click on Home, Wait for 60 seconds for it to expire. Click on the admin link again. This time, verifyJWT on the server side will detect that it has an expired refresh token:

The backend will return a 403 to the client. The client response interceptor gets the 403 and proceeds to do a request for a new refresh token:

It will execute its refresh request to the backend. And later when the backend returns the refresh token, put it in our previous request’s header. Then use Axios’s axiosPrivate to execute the previous request again.

First…let’s look at the refresh request to the backend.

We hit the backend on /refresh, which calls handleRefreshToken. We reference the cookie’s jwt first..keep a handle on it. Then we always clear the client’s cookie jwt.
We look to see if our reference to the cookie’s refresh token exist in any of our users.

Unfortunately, it doesn’t. This we detected a token reuse. So we decode the token to find out which user this refresh token belongs to.

We get the decoded’s username and try to find it in our Users. The reason why is because we want to clear all of this user’s refresh tokens and have them sign in again.
This will make the hacker’s refresh token unusable. And forces our valid user to re-sign in and get a new refresh token to use. And if that’s the case, a 403 will redirect to the /login page again.

Let’s now attempt to use the expired refresh token

Open up Postman,

GET localhost:3500/refresh

Simply paste in the refresh token. Let’s save it and use it by clicking on ‘Send’.

If any of the situation where the user does not exist, or if the username from the decoded token does not match what we have in Users, we return 403 and have them login again.

We get forbidden.

useFocusEffect

Without React.useCallback

When you go onto the page, it will focus, and thus run the effect, and run the subscribe.

When you leave the page, it will run cleanup and thus un-subscribe.

With React.useCallback

If we have a state that is in dependency array, and that state changes, then it will make the React.useCallback re-generate.