Integrating Redux in Ionic React typescript for state management
The first question that comes to mind is what is state? Think of state as the condition of your app at any specific time. It answers questions like:
- Is my textbox filled?
- Is the current user logged in? A state holds variables that define an application's condition and therefore needs to be managed. That's where redux comes in.
Redux offers a predictable state container for javascript apps.
Glossary
- Reducer: specifies state changes in response to actions performed on the app.
- Action: Actions are payloads of information that send data from your application to your store. They are the only source of information for the store
Gettings Started/ Installations
Assumptions
- You have working knowledge:
- React
- Ionic
- Typescript
- NPM
Start by installing Redux into your existing ionic-react app:
npm install --save redux
npm install --save react-redux
For the purpose of structure and maintainability, we would be taking an opinionated approach and hence be installing redux toolkit. Redux toolkit allows for easy debugging and helps in structuring your application store.
npm install --save @reduxjs/toolkit
npm i -D @types/react-redux
npm install --save-dev @types/webpack-env
The last command above helps fix an issue with webpack and module.hot as would be seen further down.
Our redux folder structure:
- actions
- reducers
- store.ts
Let the work begin
reducers/index.ts
import { combineReducers } from "@reduxjs/toolkit";
const rootReducer = combineReducers({});
export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;
This file serves as the single point of export of all the reducers.
The empty object in const rootReducer = combineReducers({});
would contain the reducers to be exported.
store.ts
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./reducers";
const store = configureStore({
reducer: rootReducer
});
if (process.env.NODE_ENV === "development" && module.hot) {
module.hot.accept("./reducers", () => {
const newRootReducer = require("./reducers").default;
store.replaceReducer(newRootReducer);
});
}
export type AppDispatch = typeof store.dispatch;
export default store;
This handles creating the store as well as its initialization. The first block handles bootstrapping the store with the reducer. The second block handles hot reloading/ compiling, allowing re-importing the new version of the root reducer function whenever it's been recompiled. It also tells the store to use the new version instead.
We would be making changes to the index.ts file (Entrypoint to the app) to use the store.
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store";
import * as serviceWorker from "./serviceWorker";
const render = () => {
const App = require("./App").default;
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
};
render();
if (process.env.NODE_ENV === "development" && module.hot) {
module.hot.accept("./App", render);
}
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
A provider is added to make the Redux store available to connect() calls in the component hierarchy.
Creating an action - reducer
We would be creating an action - reducer combination that controls a simple loading screen and alert dialog.
action/uiel.ts
import { createAction } from "@reduxjs/toolkit";
interface Loader {
isLoading: boolean;
}
interface Alert {
isVisible: boolean;
message: string;
}
export const setLoader = createAction("LOADER", function prepare(
loader: Loader
) {
return {
payload: loader
};
});
export const setAlert = createAction("ALERT", function prepare(alert: Alert) {
return {
payload: alert
};
});
reducers/uiel.ts
import { createReducer } from "@reduxjs/toolkit";
import { setLoader, setAlert } from "../actions/uiel";
const loader = {
isLoading: false
};
const alert = {
isVisible: false,
message: ""
};
export const UielReducer = createReducer(
{ loader, alert },
{
[setLoader.type]: (state, action) => ({
...state,
loader: { ...action.payload }
}),
[setAlert.type]: (state, action) => ({
...state,
alert: { ...action.payload }
})
}
);
In the createReducer method call, the first argument is a JSON object representing the initial state followed by a JSON of methods for mutation. The keys for these methods are gotten from the action types. A mutation takes the initial state and action which contains a payload as parameters.
With the action and reducer above, the reducer would be imported into reducers/index.ts
in the combineReducers function call as seen below:
import { combineReducers } from '@reduxjs/toolkit';
import { UielReducer } from './uiel';
const rootReducer = combineReducers({
uiel: UielReducer
});
export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;
Now, we have Redux setup. Let's try dispatching some actions and getting values from the state. We would be creating a HomePage component.
components/HomePage.ts
import React from 'react';
import { connect } from 'react-redux';
import { setLoader } from '../actions/uiel';
const HomePage: React.FC = (props) => {
useEffect(() => {
setTimeout(() => props.dispatch(setLoader({ isLoading: true })), 2000);
},[])
return (
{props.isLoading?
<div>
<p>The page is loading... Please wait.</p>
</div> :
null
}
)
}
export default connect((props: any) => ({
isLoading: props.uiel.loader.isLoading
}))(HomePage);
The HomePage component's props is injected with {isLoading: props.uiel.loader.isLoading}
and a dispatch object. Store value for loading can be retrieved by calling props.isLoading
while an action can be dispatched by calling props.dispatch
with an action passed in as a parameter as seen in the code snippet above.
If you need a website for your business, web or mobile application, head over to https://zhaptek.com and let's get talking or sign up as a referral and get 10% for every successful client referred. ๐
Leave comments below! ๐