React-Redux Hooks With Typescript In 2022

React Hooks
React-Redux Hooks With Typescript In 2022

This is going to be a continued version of our previous blog, React Hooks with Typescript. So if you are new to hooks, we would suggest looking into that article first, which talks about setting up the starter kit of React hooks with typescript and AntD for UI components.

If you know the basics of React hooks, like useEffect or useState, you can even give it a skip.

React-Redux Hooks

Since React push using functional components, a lot of libraries around react start publishing their own hooks, Redux being one of them, as almost most React projects use Redux these days.

The Redux hooks APIs provide an alternative to connect HOC and go away with mapStateToProps, and mapDispatchToProps, and I must say these hooks APIs provide a much cleaner way of doing that.

Now without going into much theory, let’s deep dive into the coding part.

create a new component file src/components/ReduxHooksComponent.tsx, if not already with 2 input fields and a submit button

Create A Normal Functional Component

import React, {
	ChangeEvent,
	FormEvent,
	useState,
	useEffect
} from "react";
import {
	Form,
	Input,
	Button
} from "antd";

interface Props {}

const ReduxHooksComponent: React.FC < Props > = () => {


	return ( <
		Form layout = "inline" >
		<
		Form.Item >
		<
		Input type = "text"
		placeholder = "name" / >
		<
		Input type = "text"
		placeholder = "address" / >
		<
		Button htmlType = "submit"
		type = "primary" > Submit < /Button> <
		/Form.Item> <
		/Form>
	)
};

export default ReduxHooksComponent;

Now import this component in App.tsx

import React from 'react';
import './App.css';
import ReduxHooksComponent from "./components/ReduxHooksComponent";

const App: React.FC = () => {
	return ( <
		div className = "App" >
		<
		ReduxHooksComponent / >
		<
		/div>
	);
};

export default App;

Pretty Simple. Right! after running the code it should render a component with 2 input and a submit button.

Setting up the store, actions, and reducers.

Firstly add redux and react-redux to the project

yarn add react-redux @types/react-redux redux

Create two files, src/store/index.ts and src/store/root-reducer.ts

let’s start creating each and every component of the root reducer, which would be actions, states, reducers

# src / store / root - reducer.ts

import {
	Action,
	Reducer
} from "redux";

export interface InitialState {
	name: string;
	address: string;
}

export const initialState: InitialState = {
	name: '',
	address: '',
};

export interface DispatchAction extends Action {
	payload: Partial < InitialState > ;
}

export const rootReducer: Reducer < InitialState, DispatchAction > = (state, action) => {
	return initialState;
};

Now we have a simple reducer that does nothing but returns the initial state.

Let’s create a store using this rootReducer, so our src/store/index.ts will look like

import {DispatchAction, InitialState, rootReducer} from "./root-reducer";
import {createStore} from "redux";


export const store = createStore<InitialState, DispatchAction, null, null>(rootReducer);

Also, updating the index.tsx file to wrap App with Provider and provide the store to the provider.

# src/index.tsx
........
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
........

That’s all, well you won’t see any visible changes on the browser, but eventually, you have integrated a redux into your react code.

Now let’s create some actions. Update the root-reducer.ts so that it should look something like

# src / store / root - reducer.ts
	.......
export interface DispatchAction extends Action < ActionType > {
	payload: Partial < InitialState > ;
}

export enum ActionType {
	UpdateName,
	UpdateAddress,
	DeleteName,
	DeleteAddress,
}

export const rootReducer: Reducer < InitialState, DispatchAction > = (state = initialState, action) => {
	if (action.type === ActionType.UpdateName) {
		return {
			...state,
			name: action.payload.name || ''
		};
	} else if (action.type === ActionType.DeleteName) {
		return {
			...state,
			name: ''
		};
	} else if (action.type === ActionType.DeleteAddress) {
		return {
			...state,
			address: ''
		};
	} else if (action.type === ActionType.UpdateAddress) {
		return {
			...state,
			name: action.payload.name || ''
		};
	} else return state;
};

Pretty Simple, Yes! We have just returned an updated version of the state with new values as per the actions suggest.

Lets create a Dispatcher too in the same file

src / store / root - redux.ts
	.......
export class RootDispatcher {

	private readonly dispatch: Dispatch < DispatchAction > ;

	constructor(dispatch: Dispatch < DispatchAction > ) {
		this.dispatch = dispatch;
	}
	updateName = (name: string) => this.dispatch({
		type: ActionType.UpdateName,
		payload: {
			name
		}
	});

	updateAddress = (address: string) => this.dispatch({
		type: ActionType.UpdateAddress,
		payload: {
			address
		}
	});

	deleteName = () => this.dispatch({
		type: ActionType.DeleteName,
		payload: {}
	});

	deleteAddress = () => this.dispatch({
		type: ActionType.DeleteAddress,
		payload: {}
	})
}
......

Well, now we are done with all the dispatchers, actions, stores. basically our entire redux is set up. Now all we need to do is dispatch actions from components and use values from the store.

Using Dispatcher and Store in Components via Hooks

Redux hooks provide 2 main hooks, useSelector and useDispatch

useSelector gives access to the entire store object, and we can select only what we are interested in

useDispatch gives access to dispatch, which will be used to create the Dispatcher object of RootDispatcher and then dispatch events to update the state.

Let’s create an interface of all the properties we are interested in accessing from the store,

interface StateProps {
    name: string;
    address: string;
}

UseSelector to assign name and address state

    const {name, address} = useSelector<InitialState, StateProps>((state: InitialState) => {
        return {
            name: state.name,
            address: state.address
        }
    });

Now useDispatch to get a dispatch object and create a new instance of RootDispatcher

const dispatch = useDispatch();
const rootDispatcher = new RootDispatcher(dispatch);

After integrating both state and dispatcher from to the component our file will look something like

#src / components / ReduxHooksComponent.tsx

import React, {
	ChangeEvent,
	FormEvent,
	useState,
	useEffect
} from "react";
import {
	Form,
	Input,
	Button
} from "antd";
import {
	useDispatch,
	useSelector
} from "react-redux";
import {
	InitialState,
	RootDispatcher
} from "../store/root-reducer";

interface Props {}

interface StateProps {
	name: string;
	address: string;
}

const ReduxHooksComponent: React.FC < Props > = () => {

	const {
		name,
		address
	} = useSelector < InitialState, StateProps > ((state: InitialState) => {
		return {
			name: state.name,
			address: state.address
		}
	});

	const dispatch = useDispatch();
	const rootDispatcher = new RootDispatcher(dispatch);


	return ( <
		Form layout = "inline" >
		<
		Form.Item >
		<
		Input type = "text"
		placeholder = "name"
		value = {
			name
		}
		onChange = {
			(e: ChangeEvent < HTMLInputElement > ) => {
				rootDispatcher.updateName(e.target.value)
			}
		}
		/> <
		Input type = "text"
		placeholder = "address"
		value = {
			address
		}
		onChange = {
			(e: ChangeEvent < HTMLInputElement > ) => {
				rootDispatcher.updateAddress(e.target.value)
			}
		}
		/> <
		Button htmlType = "submit"
		type = "primary" > Submit < /Button> <
		/Form.Item> <
		/Form>
	)
};

export default ReduxHooksComponent;

NOTE: The selector is approximately equivalent to the mapStateToProps argument to connect conceptually. The selector will be called with the entire Redux store state as its only argument. The selector will be run whenever the function component renders. useSelector() will also subscribe to the Redux store, and run your selector whenever an action is dispatched.

useSelector() uses strict === reference equality checks by default, not shallow equality, to use shallow Equality, we can use shallowEqual from react-redux

so our selector code would look something like

    const {name, address} = useSelector<InitialState, StateProps>((state: InitialState) => {
        return {
            name: state.name,
            address: state.address
        }
    }, shallowEqual);

To Sum Up…

We hope that we are able to help you more to deep dive into the world of React-Redux Hooks and that you will be able to apply everything you have learned here to practical use. Keep coming back to Codersera to evolve your learning process and become better each day!

FAQ

Q1. Should I use TypeScript with react Redux?

Ans- We strongly recommend using TypeScript in Redux applications. However, like all tools, TypeScript has tradeoffs. It adds complexity in terms of writing additional code, understanding TS syntax, and building the application.

Q2. Can I use TypeScript with React?

Ans- TypeScript is now very good for React if you use hooks with it.

Q3. Is TypeScript good for React?

Ans- Over the past few years, TypeScript has gain immense popularity among frontend developers. Improved maintainability, code consistency, and future browser support are few reasons behind its success