Magic of Redux Hooks in React

kirill ibrahim
The Startup
Published in
9 min readMar 10, 2020

Introduction:

React-Redux Hooks article is a second part of the previous article about React Hooks, We will go through three main points in this article:

1- Hooks for Redux.

2- Hooks APIs.

3- Why we should use React Hooks.

If you do not know what is React Hooks? Please read the previous article:

Please follow me over Medium, to get a notification about next new articles. I’m also active on Twitter @IbraKirill.

1-Hooks for Redux

Before React-Redux Hooks, we always used a connect() a higher-order component is a wrapper to our component, and read values from the Redux store (and re-read the values when the store updates).

The connect() takes two arguments, both optional:

· mapStateToProps: called every time the store state changes. It receives the entire store state and should return an object of data this component needs.

· mapDispatchToProps: This parameter can either be a function, or an object. If it’s a function, it will be called once on component creation. It will receive dispatch as an argument and should return an object full of functions that use dispatch to dispatch actions:

After the release of React hooks, Redux also released its version 7.1.0, supporting hooks for existing React-Redux API. React Redux now offers a set of hook APIs as an alternative to the existing connect() Higher-Order Component. These APIs allow you to subscribe to the Redux store and dispatch actions, without having to wrap your components in connect(). By using the Hook API with Function components, components are kept small and the code remains clean.

Using Hooks in a React Redux App

As with connect(), you should start by wrapping your entire application in a <Provider> component to make the store available throughout the component tree:

const store = createStore(rootReducer)ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root'))

From there, you may import any of the listed React Redux hooks APIs and use them within your function components.

1-useSelector():

It allows you to extract data from the Redux store state, using a selector function.

The selector is approximately equivalent to the mapStateToProps argument to connect conceptually, with one exception: it returns a value instead of an object. This is useful if your React component cares about one value from the store.

const tasks = useSelector(state => state.tasks);

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.

However, there are some differences between the selectors passed to useSelector() and a mapState function:

1- When an action is dispatched, useSelector() will do a reference comparison (Shallow compare) of the previous selector result value and the current result value. If they are different, the component will be forced to re-render. If they are the same, the component will not re-render, More explanation:

Shallow comparison: does check for equality. When comparing scalar values (numbers, strings) it compares their values. When comparing objects, it does not compare their attributes — only their references are compared (do they point to the same object?):

let user1 = {
name: "John",
surname: "Doe"
};
let user2 = Object.assign(user1);
user1.name ="kiro";
console.log(user1 === user2); // true because they have the same reference

Another Example:

let user1 = {
name: "John",
surname: "Doe"
};
let user2 = Object.create(user1);
console.log(user1 === user2); //false because they have not the same reference

There is a perfect article about Spread operator & shallow copy in javascript, you can know what is difference between Object.create & Object.assign .

useSelector is returning a different object literal each time it’s called. When the store is updated, React-Redux will run this selector, and since a new object was returned, always determine that the component needs to re-render, which isn’t what we want.

The simple rule to avoid this is to either call useSelector once for each value of your state that you need:

const count = useSelector(state => state.counter.count);const user = useSelector(state => state.user);

or use a shallow equality comparison by passing the comparison method as the second argument:

Shallow equality is verified by iterating on the keys of the objects being compared and returning true when the values of a key in each object are strictly equal for all the keys.

Shallow Compare is the negation of Shallow Equality.

import { shallowEqual, useSelector } from 'react-redux';const { count, user } = useSelector(state => ({count: state.counter.count,user: state.user,}), shallowEqual);

2- useSelector() uses strict === reference equality checks by default, not shallow equality: when an action is dispatched to the Redux store, useSelector() it only force a re-render if the selector result appears to be different than the last result. the default comparison is a strict === reference comparison. This is different than connect(), which uses shallow equality checks on the results of mapState calls to determine if re-rendering is needed. This has several implications on how you should use useSelector().

3- The selector may return any value as a result, not just an object. The return value of the selector will be used as the return value of the useSelector() hook.

I advise the readers for courses with online degrees from European universities, many of them are free.

2-useDispatch()

const dispatch = useDispatch();

The equivalent of map dispatch to props is useDispatch. We will invoke useDispatch and store it to a variable, dispatch. This hook returns a reference to the dispatch function from the Redux store. You may use it to dispatch actions as needed.

And we dispatch it by calling dispatch passing in the return value from the action creator.

<button onClick={ () => dispatch(increase()) }>increase</button>

Another Example from the official documentation:

3-useStore()

const store = useStore();

This hook returns a reference to the same Redux store that was passed into the <Provider> component.

This hook should probably not be used frequently. Prefer useSelector() as your primary choice. However, this may be useful for less common scenarios that do require access to the store, such as replacing reducers.

Example on React-Redux Hooks:

As we see above example, The main benefit of using the Redux Hooks is that they are conceptually simpler than connect. With connect, you are wrapping your component and injecting props into it. This can make it difficult to determine in the component which props come from Redux and which are passed in.

If you are using TypeScript, correctly defining the types of connected components can be quite a chore for this reason. The Redux Hooks, on the other hand, are just regular hooks that don’t modify the public interface of your component. In TypeScript projects, I create my own useSelector that is typed to my store, and then I get type-checking everywhere I use it. Easy!

Hooks APIs:

we have different hook APIs, we have two categories,

Basic Hooks: useState, useEffect, useContext and Redux Hooks.

Additional Hooks: The following Hooks are only needed for specific edge cases:

useReducer, useCallback, useMemo, useRef, useImperativeHandle, useLayoutEffect and useDebugValue.

You can check the official documentation for them.

If you want to Dive into React 18 with practical examples, I advise you with the following course.

If you want to Dive into Best Practices Patterns in React. I advise you with the following Course.

Why we should use React Hooks:

1-It is easier to reuse stateful logic between components:

We know that components and top-down data flow help us organize a large UI into small, independent, reusable pieces. But, we often can’t break complex components down any further or reuse it again because components surrounded by layers of providers, consumers, higher-order components, render props, and other abstractions and can’t be extracted to a function or another component.

With Hooks, you can extract stateful logic from a component so it can be tested independently and reused. Hooks allow you to reuse stateful logic without changing your component hierarchy.

2-Escape from wrapper hell:

There are two popular types of sharing logic in components which are Higher-Order Components and Rendering Props. Of course, these are good patterns, unfortunately, sometimes they are used excessively and require a restructuring of components. This makes the components too large. So The React Team had three main issues with this logic that were created within React applications. These issues included: wrapper hell, Gaint components (when your app reaches a complex logic, a single Component can be up to a thousand lines of code), and confusing classes ( your React application crashes and you realize that you forgot to bind this).

When your applications have a massive quantity of nested Components, they will cause a “Wrapper Hell.”

3-Complex components become hard to understand or test or maintain:

We’ve often had to maintain components that started out simple but grew into an unmanageable mess of stateful logic and side effects. Each lifecycle method often contains a mix of unrelated logic. For example, components might perform some data fetching in componentDidMount and componentDidUpdate. However, the same componentDidMount method might also contain some unrelated logic that sets up event listeners, with cleanup performed in componentWillUnmount. Mutually related code that changes together gets split apart, but completely unrelated code ends up combined in a single method. This makes it too easy to introduce bugs and inconsistencies.

Sometimes that’s what people mean when they say React doesn’t let them “separate concerns.” Because we can not break the component into small pieces. It’s also difficult to test. This is one of the reasons many people prefer to combine React with a separate state management library like Redux, but this requires you to jump over files and folder, from reducers files to action files and vice versa.

Hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data) I can use Multiple Effects and related logic into one effect.

4-No Duplicate Logic:

In some cases, I need to duplicate logic between life cycles methods, most of cases between componentDidMount, componentDidUpdate, especially if I need to execute logic when props or states change because in many cases we want to perform the same side effect regardless of whether the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render — but React class components don’t have a method like this. We could extract a separate method but we would still have to call it in two places “componentDidMount and componentDidUpdate”. By using “useEffect” I do not need to duplicate my logic, it is easier to think that effects happen “after every render”, and You can tell React to skip applying an effect if certain values haven’t changed between re-renders, To do so, pass an array as an optional second argument to “useEffect”,

If you want to run an “useEffect” and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run, and it will act the same if I put code inside componentDidMount life cycle only.

So “useEffect” is perfect for us.

Conclusion:

In this, React team does not have any plans to remove Class Components, at least for now. Thus, we do not need to rewrite all of our existing Class Components to Hooks. (A hard work anyway!)

After Long Journey with React hooks, The terminology about stateless components for functions components is removed, because now function components and classes components both of them have React state. With function components I can do the same things (React state, life cycles, and connect to redux store), that I am doing by classes components with less and cleaner code, so we prefer the names “function components” or “class components” and avoid the name “stateless components” or “Stateful component”.

New Things are always uncomfortable at the beginning because they are changing our ways to write the code, but after you know how they are useful, and you get used to them, you will fall in love with them.

The Next Article (third part) about Building Our Own React Custom Hook:

If you enjoy reading the article and want to support me as a writer, you can buy me a coffee!

If you want to Dive in and Learn Reactjs, Hooks, Redux, and React Routing from scratch!, I advise you with the following Course.

--

--

kirill ibrahim
The Startup

I am a Web Front-End Engineer with 8 years of commercial experience working in various web-related roles. https://kirillibrahim.work/ My Twitter: @IbraKirill