{
    "componentChunkName": "component---src-templates-blog-post-js",
    "path": "/react-tutorial-using-redux-saga-typescript/",
    "result": {"data":{"site":{"siteMetadata":{"title":"CrewCode Solutions"}},"markdownRemark":{"id":"a48018e9-44ac-53f0-a03b-da9bde9fb047","excerpt":"In this article i will explain you step by step to use redux saga in react typescript application Step 1: Create react typescript application with following…","html":"<p>In this article i will explain you step by step to use redux saga in react typescript application</p>\n<h4>Step 1: Create react typescript application with following command</h4>\n<pre><code class=\"language-js\">npx create-react-app my-app --template typescript\n</code></pre>\n<h4>Step 2: Install redux, redux saga, redux logger, axios in your newly created application</h4>\n<p>In your newly created application install redux , redux saga with following command</p>\n<pre><code class=\"language-js\">npm install --save redux react-redux redux-saga @types/react-redux @types/redux-saga\n</code></pre>\n<p>Install redux logger this helps in to log all triggered actions in the developer console</p>\n<pre><code class=\"language-js\">npm install --save-dev redux-logger @types/redux-logger\n</code></pre>\n<p>Install axios this helps in sending http request</p>\n<pre><code class=\"language-js\">npm install --save axios @types/axios\n</code></pre>\n<h4>Step 3: Create Store</h4>\n<p>Store act as central repository for all your state management</p>\n<p>Go to your project folder and create <strong><em>/src/store/index.ts</em></strong> this is the main file for your store creation where we will be initializing and creating store</p>\n<pre><code class=\"language-js\">import { createStore, applyMiddleware } from \"redux\";\nimport createSagaMiddleware from \"redux-saga\";\nimport logger from \"redux-logger\";\n\nimport rootReducer from \"./rootReducer\";\nimport { rootSaga } from \"./rootSaga\";\n\n// Create the saga middleware\nconst sagaMiddleware = createSagaMiddleware();\n\n// Mount it on the Store\nconst store = createStore(rootReducer, applyMiddleware(sagaMiddleware, logger));\n\n// Run the saga\nsagaMiddleware.run(rootSaga);\n\nexport default store;\n</code></pre>\n<h4>Step 4: Create Root Reducer</h4>\n<p>Reducers are the pure functions that take the current state and action and return the new state and tell the store how to do</p>\n<p>Create a main reducer file as <strong><em>/src/store/rootReducer.ts</em></strong> where we will combine all the sub reducers and export it from here. Copy paste following content inside your rootReducer</p>\n<pre><code class=\"language-js\">import { combineReducers } from \"redux\";\n\nimport authReducer from \"./auth/reducer\";\n\nconst rootReducer = combineReducers({\n  auth: authReducer,\n});\n\nexport type AuthState = ReturnType&#x3C;typeof rootReducer>;\n\nexport default rootReducer;\n</code></pre>\n<h4>Step 5: Create Auth Reducer</h4>\n<p>We will be creating auth reducer as sub reducer i'm creating an example of login and signup to show you all how we can work for authentication in redux saga</p>\n<p>Create a file <strong><em>/src/store/auth/reducer.ts</em></strong> and copy paste the following code</p>\n<pre><code class=\"language-js\">import {\n  LOGIN_REQUEST,\n  LOGIN_SUCCESS,\n  LOGIN_FAILURE,\n  SIGNUP_REQUEST,\n  SIGNUP_SUCCESS,\n  SIGNUP_FAILURE,\n} from \"./actionTypes\";\n\nimport { AuthActions, AuthState } from \"./types\";\n\nconst initialState: AuthState = {\n  pending: false,\n  token: \"\",\n  error: null,\n};\n\nconst reducers = (state = initialState, action: AuthActions) => {\n  switch (action.type) {\n    case SIGNUP_REQUEST:\n      return {\n        ...state,\n        pending: true,\n      };\n    case SIGNUP_FAILURE:\n      return {\n        ...state,\n        pending: false,\n        token: \"\",\n        error: action.payload.error,\n      };\n\n    case SIGNUP_SUCCESS:\n      return {\n        ...state,\n        pending: false,\n        token: action.payload.token,\n        error: null,\n      };\n\n    case LOGIN_REQUEST:\n      return {\n        ...state,\n        pending: true,\n      };\n    case LOGIN_SUCCESS:\n      return {\n        ...state,\n        pending: false,\n        token: action.payload.token,\n        error: null,\n      };\n    case LOGIN_FAILURE:\n      return {\n        ...state,\n        pending: false,\n        token: \"\",\n        error: action.payload.error,\n      };\n    default:\n      return {\n        ...state,\n      };\n  }\n};\n\nexport default reducers;\n</code></pre>\n<h4>Step 6: Create Auth Action types</h4>\n<p>Actions are plain JavaScript object that must have a type attribute to indicate the type of action performed. It tells us what had happened. Types should be defined as string constants in your application as given below</p>\n<p>Create a file <strong><em>/src/store/auth/actionTypes.ts</em></strong> and copy paste following code inside that file</p>\n<pre><code class=\"language-js\">export const LOGIN_REQUEST = \"LOGIN_REQUEST\";\nexport const LOGIN_SUCCESS = \"LOGIN_SUCCESS\";\nexport const LOGIN_FAILURE = \"LOGIN_FAILURE\";\n\nexport const SIGNUP_REQUEST = \"SIGNUP_REQUEST\";\nexport const SIGNUP_SUCCESS = \"SIGNUP_SUCCESS\";\nexport const SIGNUP_FAILURE = \"SIGNUP_FAILURE\";\n</code></pre>\n<h4>Step 7: Add types for Auth</h4>\n<p>Create a file <strong><em>/src/auth/store/auth/types.ts</em></strong> and copy paste following code</p>\n<pre><code class=\"language-js\">import {\n  LOGIN_REQUEST,\n  LOGIN_SUCCESS,\n  LOGIN_FAILURE,\n  SIGNUP_REQUEST,\n  SIGNUP_SUCCESS,\n  SIGNUP_FAILURE,\n} from \"./actionTypes\";\n\nexport interface IAuth {\n  token: string;\n}\n\nexport interface AuthState {\n  pending: boolean;\n  token: string;\n  error: string | null;\n}\n\nexport interface LoginPayload {\n  values: { email: string, password: string };\n  callback: any;\n}\n\nexport interface LoginSuccessPayload {\n  token: string;\n}\n\nexport interface LoginFailurePayload {\n  error: string;\n}\n\nexport interface SignupSuccessPayload {\n  token: string;\n}\n\nexport interface SignupFailurePayload {\n  error: string;\n}\n\nexport interface LoginRequest {\n  type: typeof LOGIN_REQUEST;\n  payload: LoginPayload;\n}\n\nexport type LoginSuccess = {\n  type: typeof LOGIN_SUCCESS,\n  payload: LoginSuccessPayload,\n};\n\nexport type LoginFailure = {\n  type: typeof LOGIN_FAILURE,\n  payload: LoginFailurePayload,\n};\n\nexport interface SignupPayload {\n  values: { email: string, password: string };\n  callback: any;\n}\n\nexport interface SignupRequest {\n  type: typeof SIGNUP_REQUEST;\n  payload: SignupPayload;\n}\n\nexport type SignupSuccess = {\n  type: typeof SIGNUP_SUCCESS,\n  payload: SignupSuccessPayload,\n};\n\nexport type SignupFailure = {\n  type: typeof SIGNUP_FAILURE,\n  payload: SignupFailurePayload,\n};\n\nexport type AuthActions =\n  | LoginRequest\n  | LoginSuccess\n  | LoginFailure\n  | SignupFailure\n  | SignupSuccess\n  | SignupRequest;\n</code></pre>\n<h4>Step 8: Create Actions for Auth</h4>\n<p>Actions are generally passing of the payload inn form of object with action types which generally tells which action has to be done</p>\n<p>Create a file <strong><em>/src/store/auth/actions.ts</em></strong> and copy paste following code</p>\n<pre><code class=\"language-js\">import {\n  LOGIN_REQUEST,\n  LOGIN_FAILURE,\n  LOGIN_SUCCESS,\n  SIGNUP_REQUEST,\n  SIGNUP_FAILURE,\n  SIGNUP_SUCCESS,\n} from \"./actionTypes\";\nimport {\n  LoginPayload,\n  SignupPayload,\n  LoginRequest,\n  LoginSuccess,\n  LoginSuccessPayload,\n  LoginFailure,\n  LoginFailurePayload,\n  SignupRequest,\n  SignupSuccess,\n  SignupSuccessPayload,\n  SignupFailure,\n  SignupFailurePayload,\n} from \"./types\";\n\nexport const loginRequest = (payload: LoginPayload): LoginRequest => ({\n  type: LOGIN_REQUEST,\n  payload,\n});\n\nexport const loginSuccess = (payload: LoginSuccessPayload): LoginSuccess => ({\n  type: LOGIN_SUCCESS,\n  payload,\n});\n\nexport const loginFailure = (payload: LoginFailurePayload): LoginFailure => ({\n  type: LOGIN_FAILURE,\n  payload,\n});\n\nexport const signupRequest = (payload: SignupPayload): SignupRequest => ({\n  type: SIGNUP_REQUEST,\n  payload,\n});\n\nexport const signupSuccess = (\n  payload: SignupSuccessPayload\n): SignupSuccess => ({\n  type: SIGNUP_SUCCESS,\n  payload,\n});\n\nexport const signupFailure = (\n  payload: SignupFailurePayload\n): SignupFailure => ({\n  type: SIGNUP_FAILURE,\n  payload,\n});\n</code></pre>\n<h4>Step 9: Create Saga middleware for Auth</h4>\n<p>Redux Saga is a middleware library used to allow a Redux store to interact with resources outside of itself asynchronously</p>\n<p>Create a file <strong><em>/src/store/auth/saga.ts</em></strong> and copy paste the following code</p>\n<pre><code class=\"language-js\">import axios from \"axios\";\nimport { all, call, put, takeLatest } from \"redux-saga/effects\";\n\nimport {\n  loginFailure,\n  loginSuccess,\n  signupSuccess,\n  signupFailure,\n} from \"./actions\";\nimport { LOGIN_REQUEST, SIGNUP_REQUEST } from \"./actionTypes\";\nimport { IAuth } from \"./types\";\n\nconst login = async (payload: { email: string; password: string }) => {\n  const { data } = await axios.post&#x3C;IAuth>(\n    \"https://reqres.in/api/login\",\n    { email: payload.email, password: payload.password },\n    {\n      headers: {\n        \"Content-Type\": \"application/json\",\n        Accept: \"application/json\",\n      },\n    }\n  );\n  return data;\n};\n\nconst signup = async (payload: { email: string; password: string }) => {\n  const { data } = await axios.post&#x3C;IAuth>(\n    \"https://reqres.in/api/register\",\n    { ...payload },\n    {\n      headers: {\n        \"Content-Type\": \"application/json\",\n        Accept: \"application/json\",\n      },\n    }\n  );\n\n  return data;\n};\n\n\nfunction* loginSaga(action: any) {\n  try {\n    const response: { token: string } = yield call(login, {\n      email: action.payload.values.email,\n      password: action.payload.values.password,\n    });\n\n    yield put(\n      loginSuccess({\n        token: response.token,\n      })\n    );\n    action.payload.callback(response.token);\n  } catch (e: any) {\n    yield put(\n      loginFailure({\n        error: e.message,\n      })\n    );\n  }\n}\n\nfunction* signupSaga(action: any) {\n  try {\n    const response: { token: string } = yield call(signup, {\n      email: action.payload.values.email,\n      password: action.payload.values.password,\n    });\n\n    yield put(\n      signupSuccess({\n        token: response.token,\n      })\n    );\n    action.payload.callback(response.token);\n  } catch (e: any) {\n    yield put(\n      signupFailure({\n        error: e.message,\n      })\n    );\n  }\n}\n\nfunction* authSaga() {\n  yield all([takeLatest(LOGIN_REQUEST, loginSaga)]);\n  yield all([takeLatest(SIGNUP_REQUEST, signupSaga)]);\n}\n\nexport default authSaga;\n\n</code></pre>\n<h4>Step 10: Create Root saga which combine all the saga and export</h4>\n<p>Create a file <strong><em>/src/store/rootSaga.ts</em></strong> and copy paste the following code</p>\n<pre><code class=\"language-js\">import { all, fork } from \"redux-saga/effects\";\n\nimport authSaga from \"./auth/sagas\";\n\nexport function* rootSaga() {\n  yield all([fork(authSaga)]);\n}\n</code></pre>\n<h4>Step 11: Add Reselect</h4>\n<p>Using reselect has certain advantage it create memoization of the selector that are only re executed when arguments of the selector changes</p>\n<p>Install reselect using following command</p>\n<pre><code class=\"language-js\">npm install --save reselect\n</code></pre>\n<p>Create a file <strong><em>/src/store/auth/selector.ts</em></strong> and copy paste the following code</p>\n<pre><code class=\"language-js\">import { createSelector } from \"reselect\";\n\nimport { AuthState } from \"../rootReducer\";\n\nconst getPending = (state: AuthState) => state.auth.pending;\n\nconst getToken = (state: AuthState) => state.auth.token;\n\nconst getError = (state: AuthState) => state.auth.error;\n\nexport const getAuthSelector = createSelector(getToken, (token) => token);\n\nexport const getPendingSelector = createSelector(\n  getPending,\n  (pending) => pending\n);\n\nexport const getErrorSelector = createSelector(getError, (error) => error);\n</code></pre>\n<h4>Step 12: Add Store provider to your index.ts file</h4>\n<p>Inside your <strong><em>src/index.tsx</em></strong> file copy paste the following code</p>\n<pre><code class=\"language-js\">import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport \"./index.css\";\nimport App from \"./App\";\nimport reportWebVitals from \"./reportWebVitals\";\nimport { Provider } from \"react-redux\";\n\n\nimport store from \"./store\";\n\nconst root = ReactDOM.createRoot(\n  document.getElementById(\"root\") as HTMLElement\n);\nroot.render(\n  &#x3C;React.StrictMode>\n    &#x3C;Provider store={store}>\n        &#x3C;App />\n    &#x3C;/Provider>\n  &#x3C;/React.StrictMode>\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n</code></pre>\n<h4>Step 13: Inside your login page use redux saga</h4>\n<pre><code class=\"language-js\">import React, { useRef } from \"react\";\nimport { loginRequest } from \"../store/auth/actions\";\nimport { connect } from \"react-redux\";\n\nconst Login: React.FC&#x3C;{}> = (props: any) => {\n  const emailRef = useRef();\n  const passwordRef = useRef();\n\n  const callback = (data: any) => {\n    console.log(\"Inside callback after login\");\n  };\n\n  const login = () => {\n    let data: any = {\n      values: {\n        email: emailRef.current.value,\n        password: passwordRef.current.value,\n      },\n      callback,\n    };\n    props.login(data);\n  };\n  return (\n    &#x3C;div>\n      &#x3C;div className=\"form-floating\">\n        &#x3C;input\n          type=\"email\"\n          className=\"form-control\"\n          name=\"email\"\n          id=\"floatingInput\"\n          placeholder=\"name@example.com\"\n          ref={emailRef}\n        />\n        &#x3C;label htmlFor=\"floatingInput\">Email address&#x3C;/label>\n      &#x3C;/div>\n\n      &#x3C;div className=\"form-floating mt-3\">\n        &#x3C;input\n          type=\"password\"\n          className=\"form-control\"\n          name=\"password\"\n          id=\"floatingPassword\"\n          placeholder=\"Password\"\n          ref={passwordRef}\n        />\n        &#x3C;label htmlFor=\"floatingPassword\">Password&#x3C;/label>\n      &#x3C;/div>\n\n      &#x3C;div className=\"checkbox mb-3 mt-3\">\n        &#x3C;label>\n          &#x3C;input name=\"remember\" type=\"checkbox\" defaultValue=\"remember-me\" />{\" \"}\n          Remember me\n        &#x3C;/label>\n      &#x3C;/div>\n      &#x3C;button\n        onClick={() => {\n          login();\n        }}\n        className=\"w-100 btn btn-lg btn-warning\"\n      >\n        Sign in\n      &#x3C;/button>\n    &#x3C;/div>\n  );\n};\n\nconst mapDispatchToProps = (dispatch: any) => ({\n  login: (params: any) => dispatch(loginRequest(params)),\n});\n\nexport default connect(null, mapDispatchToProps)(Login);\n</code></pre>","fields":{"slug":"/react-tutorial-using-redux-saga-typescript/"},"frontmatter":{"title":"React tutorial using redux saga in react typescript application","date":"April 16, 2023","description":"In this article i will explain you step by step to setup redux saga in typescript application","bannerimage":"https://crew-code-images.s3.us-east-1.amazonaws.com/blog_images/React.jpg"}},"previous":{"fields":{"slug":"/react-tutorial-using-react-router6-typescript/"},"frontmatter":{"title":"React tutorial using react router v6 in react typescript with nested routes","date":"April 04, 2023","bannerimage":"https://crew-code-images.s3.us-east-1.amazonaws.com/blog_images/React.jpg"}},"next":{"fields":{"slug":"/react-tutorial-working-with-redux-toolkit-with-saga/"},"frontmatter":{"title":"React tutorial working with redux toolkit with saga in typescript","date":"April 26, 2023","bannerimage":"https://crew-code-images.s3.us-east-1.amazonaws.com/blog_images/redux-toolkit.jpeg"}}},"pageContext":{"id":"a48018e9-44ac-53f0-a03b-da9bde9fb047","previousPostId":"72a4892b-edf3-5513-90ce-f24179cc276d","nextPostId":"cf94ca75-725d-52ee-b2ab-71f7aa5fd158"}},
    "staticQueryHashes": ["3860684146"]}