image
author

William Dawson

Full Stack Developer

This is going to be a continued version of our previous blog, React Hooks with Typescript, So if you are new to hooks, I 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 basic of react hooks, like useEffect or useState, you can even give it a skip.

Since React push using of functional components, a lot of libraries around react start publishing their own hooks, Redux being one of them, as almost most react project 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 API provide a much cleaner way of doing that.

Now without going much in theory, lets 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 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 ans 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, store. basically our entire redux is setup. 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 give access to the entire store object, and we can select only what we are interested in

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

Let’s create an interface of all the property 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);

Can I use hooks with redux?

Yes, React-Redux released its latest version 7.1 which includes useSelector and useDispatch using which you can create React-Redux app with less and more readable code.

What is useSelector hook?

useSelector, allow us to extract data from the redux store. It is equivalent to mapStateToProps, as we use in class-based component. It has two arguments one is “selector” function and the other is “equalityFn” function, which is optional.

What is useDispatch hook?

useDispatch returns a reference of dispatch function from redux store using this reference we can dispatch our actions to the reducer. dispatch function takes an object with two properties “Type” and “Payload”.

What is the second argument of useSelector hook?

The second argument is a “shallowEqual” function (imported from “react-redux”) which compares the previous result value with the current result value of the selector and the comparison is shallow i.e “==”. If you don’t pass it then it use the default strict comparison “===”.

How useful was this post?

How useful was this post?

Click on a star to rate it!

Average rating 4.5 / 5. Vote count: 12

No votes so far! Be the first to rate this post.

Please do Rate Us and Share!

Related Blogs

  • author
    Kela Casey

    Java vs Kotlin: Which One Is Better To Learn In 2021?

    Android app development has become a regular trend to boost your business, but the main factor deciding whether it’ll be a success or not is the programming language used. For a larger number of people, java is the best option because it is easily available. But, the introduction of Kotlin in the scene reduced...

  • author
    Lucas White

    How To Become A Better Node.JS Developer In 2021?

    The Node is currently the world’s most popular technology that is opening up infinite career possibilities for any developer who is looking for potential for growth in this field. You can create different applications, such as apps for social media, instant messaging platforms, apps for real-time monitoring, online gaming, and tools for collaboration. Several...

  • author
    Lucas White

    What Are The Reasons To Learn Express.js in 2021?

    Express.js is a server framework for the Node.js web application that is designed especially to create a single-page, multi-page, and hybrid web applications. For node.js, this has become the standard server system. Express is the backend portion of a component known as the MEAN stack. The MEAN is a free and open-source JavaScript software...

image

About The Author

William is a CTO and a full-stack engineer with 10 years of experience. He has spent the past seven years doing web and mobile apps. He’s good at designing architecture and implementing agile development process. The technologies he’s worked with include: Node.js, Elixir, Rails, AngularJS, React, React Native, Objective-C, iOS, Java, Android. He’s also familiar with C++, Haskell, C#/.NET. He is an enthusiastic programmer and a great guy to know

Recents

max troost says:

very nice, but why create a rootDispatcher Class? now it’s initiated every time `ReduxHooksComponent` rerenders. Won’t it be cleaner to create an action function that returns an object containing the keys `type` and `payload`?

John says:

Probably we can use a Singleton method to instantiate the object, or converting all the dispatch actions to static method and passing dispatch as parameters to each actions. Or probably we can even create the new object under useEffect.

Try our One-Week Risk Free Trial for Hiring a Coder

Know more Hire a Coder