How to learn React Hooks, React Router, and Redux

kiki
9 min readFeb 23, 2021
Photo by Christopher Gower on Unsplash

This blog is for people who already learned React basics and want to focus on learning React hooks, React Router, and Redux.

I will explain them with examples.

· React Hooks
What are React Hooks?
The Rules of React Hooks
How to Use React Hooks
· React Router
Why do we use React Router?
How to use React router
· Redux
What is Redux?
When to use Redux?
Redux workflow
Redux core concepts
Redux middleware
Connecting React with Redux
· Conclusion

Let’s get started.

React Hooks

What are React Hooks?

Before React 16.8, functional components do not have their internal state. We have to use class components to manage the internal data with the state. The new feature -react hooks, allows us to use state in function components. They are functions that hook into React state and lifecycle features of function components. And usually, we will write less code compare to using class components.

The Rules of React Hooks

  1. We can only use hooks at the top-level inside function components or within custom hooks. Do not call react hooks inside regular javascript functions.
  2. We cannot use hooks inside conditions, loops, but we can add conditions or loops inside hooks.

How to Use React Hooks

First, let’s create a react-hooks project.

npx create-react-app react-hooks

useState() hook

This hook adds React state to function components.

Inside /src/useState/ UseStateExample.jsx

  1. Import useState from React.
import React, { useState } from “react”;

2. Use useState(initialValue). This function accepts one argument which is the initial value, returns an array of 2 elements. The first element is the state variable that has the initial value; The second element is a function that will be used to update the state.

const [counter, setCounter] = useState(0);

This declares a new state variable counter. An initial value of 0 has been assigned to it. setCounter() will be used to update the counter’s value.

In the following example, setCounter(counter + 1) has been called to increase the counter value.

import React, { useState } from “react”;export default function UseStateExample() {const [counter, setCounter] = useState(0);function handleIncrease() {setCounter(counter + 1);}return (<><h1>{counter}</h1><button onClick={handleIncrease}>Increase</button></>);}

useState can be used as many times as necessary.

useEffect() hook

This hook is equivalent to a combination of lifecycle methods componentDiMount, componentDidUpate, componentWillUnmount.

It allows us to perform side effects in function components. By side effects, I mean any actions that will affect outside of the calling function.

Common side effects

  • Change the DOM directly
  • Fetch data from an API
  • Set a timer

useEffect() has two arguments.

useEffect(callback,[dependencies]);

The callback function handles the side-effect logic and will be called after the component rendering.

The second argument is an optional dependency array. It is used to optimize when the callback function will be called. After each rendering, the callback function will be executed only if any of the dependency values has been changed.

3 ways of using dependency argument

  • No dependency argument: the callback function runs after each rendering.
useEffect(() => {//code});
  • An empty array: useEffect() hook acts as componentDidMount(). It runs once after the first rendering.

The following example adds a timer after the initial rendering and cleans up the timer when the component unmounts.

Other side-effects, such as initializing table column fields can be implemented in this scenario.

  • Non-empty array: the callback function runs after each rendering as long as the dependency value changes between renderings.

In the following example, a name prop has been passed from the parent component of UseEffectExample. The fetching will be executed when the name value has been changed after each rendering.

React Router

Why do we use React Router?

For a single-page React application, it re-renders its components in response to the URL change. It does not send requests to the server to fetch new HTML.

React js is a javascript library and used to build interfaces. It does not support routing itself. Therefore, react-router needs to be implemented in order to allow the application to navigate between components.

How to use React router

There are three main categories of components in React Router:

1. Routers

The main job of a router component is to create a history object to track the location URL. When the location changes because of navigation actions, the child element will be re-rendered. Normally, we put the top-level element inside a router component, like <App/>.

Props have been passed to a regular component VS a router-wrapped component.

  • Regular component: it receives whatever has been passed to it.
  • A router-wrapped component receives history, location, and match props.

We will be focusing on routers for web applications in this blog.

<BrowserRouter> and <HashRouter>

  • The main difference between the two is the way they store the URL and communicate with your web server.
  • <BrowserRouter>should be used when you have a server that handles dynamic client-side requests.
  • <HashRouter>should be used for web applications that request static resources from the server, like files and images.

Example of using <BrowserRouter>

2. Navigation components

<Link>

It renders a navigation link. When we click on a <Link> link, only the corresponding component will be loaded without refreshing the whole page.

Example:

<Link to="/about">About</Link>

<NavLink>

It works the same as <Link> except adding a activeClassName prop to highlight the currently active element.

Example:

<NavLink to="/about" activeClassName="selected">About</NavLink>

3. Route matches

<Route>

It renders a UI component when its path matches the current URL.

When there are multiple <Route>s, we can use <Switch> to group them. <Switch> only renders the first route that matches the current URL. If no routes match, no component will be rendered. A <Redirect> can be used to render a component when no routes match.

<div className="row">     <div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<Link to="/about">About</Link>
<Link to="/home">Home</Link>

</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>

</div>
</div>
</div>

Redux

What is Redux?

Redux is a Javascript library that is used to share application state values between components. The state is data that changes from time to time. It could be data fetched from a server API; It could be items selected or inputted by users; It could be error messages rendered to the user, etc.

Redux is a stand-lone library. It can also be used with React, Angular, or Vue.

When to use Redux?

  • Sharing data between components. A component can still have its internal state.
  • One component changes another component’s state.
  • It is not mandatory to use Redux. Only use it when it makes your life easier.

Redux workflow

Redux holds all the states for an application in a central store. Each component will have access to the state without having to send props down the component hierarchy tree.

Redux core concepts

  1. action

An action is a plain JavaScript object that has been sent to the store to update the state.

It has two attributes.

  • type: describes how the state should be updated. This attribute is mandatory and should be named as type.
  • payload: optional and represents what should be updated.
{ type: ‘increment’,payload:1 }

For best practice, we wrap every action within a function and store all the action types as constants.

In Redux, the only way to change the state is to dispatch an action using store.dispatch(action).

2. reducer

A reducer is a pure JavaScript function that accepts the previous state of the application, the action that needs to be performed, and returns a new state.

In the following example, the countReducer performs addition or subtraction based on the action type.

We can use many reducers in an application. How to combine reducers together will be discussed later.

Since the redux state is immutable, for arrays and objects, we cannot update the initial state. We have to make copies of existing objects or arrays, then modify the copies.

3. store

There is only one store in an application. It is used to hold all of the application state.

A store is created using createStore() by passing a reducer. It connects actions, reducers, and states together.

Store has three main methods

  1. getState(): get the current state value
  2. dispatch(action): tell the reducer to update the state

3.subscribe(listener): reducers only update the state and do not trigger the component rendering. Therefore, components need to listen for the state changes and rendering.

Adding the following code to the index.js where renders the <App/> makes the whole application listen for the state changes.

//the whole application listens for the state changesstore.subscribe(() => {ReactDOM.render(<App />, rootElement);});

In this video, I explain how to use Redux basics.

Redux middleware

A Redux middleware is a function that used to intercept the actions before they reach the reducer. It holds the business logic here.

Basically, it is a function that returns a function, which takes next as an argument. The inner function returns another function that takes action as an argument. Finally, it returns next(action).

function checkOddSumMiddleware() {
return function(next){
return function(action){
// do your stuff
return next(action);
}
}
}

We should always call next(action) in a middleware because it forwards the action to the reducer.

In our previous example, we add one more button -Add when the sum is odd.

<button onClick={this.incrementIfOdd}>Add when sum is odd</button>&nbsp;

We add middleware to check if the sum is odd.

Then connect the middleware with the store

Inside the Count component, add the following code.

//add if the sum is oddincrementIfOdd = () => {//get the selected valueconst { value } = this.selectNumber;//dispatch an action to update the state valuestore.dispatch(createIncrementOddAction(value * 1));};

Add the action creator createIncrementOddAction inside the count_actions.js

export const createIncrementOddAction = (payload) => ({type: INCREMENTODD,payload});export const createIncrementOddAction = (payload) => ({type: INCREMENTODD,payload});

Add the INCREMENTODD constant inside the constant.js

export const INCREMENTODD = “icrementodd”;

This example code is on Github.

Connecting React with Redux

So we talked about how to use Redux alone. Now it is time to connect React and Redux together.

How do we do it? React-redux! It is used to simplify using Redux with React.

React Redux is the official React binding for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update data.

React Redux separates components into two different types.

  1. Container components

a) Manage data and business logic

b) Interact with Redux

2. UI components

a) Responsible for rendering UI and do not take care of the business logic

b)Use props to receive and send data

c) Do not use Redux API

UI components are wrapped in container components.

How to use react-redux?

We are going to revise the previous example.

Before we get started, install react-redux first (in VS code).

npm install react-redux
  1. Create a CountUI component.

Note: I just created the UI component and have not exported it yet.

2. Use connect() to wrap the UI component into a container component.

Import the connect.

//import connect from react-reduximport { connect } from "react-redux"; 

connect() receives two or three arguments depending on the use case.

  • mapStateToProps: a function that connects the Redux state to the props of the React component.
  • mapDispatchToProps: a function that connects the Redux actions to the props of a UI component.

3. Rendering the Container component

We use <Provider /> to make the whole application is available to the Redux store.

We already connected the UI component to the Redux. Let’s take a look at how to interact with the store.

  1. Get data from the store

<h1>Sum:{this.props.counter}</h1>

2. Send actions to the Redux store

Example on Github.

Conclusion

In this blog, I covered the basics concepts of React Hooks, React Router, and Redux. Hopefully, all I have said makes sense.

If there is anything wrong, please feel free to make a comment.

--

--