Redux vs Hooks: Hooking Up With State

Redux vs Hooks: Hooking Up With State

Back To The Basics: What is React?

React, the most popular front–end framework–like library nowadays, is like Freddie Mercury.

You don’t have to introduce Freddie Mercury.

Everyone has heard about them at some point. But still, for those who mess with time travel or just got back from their soul–searching adventure in some wilderness, here goes the refresher: React is a JavaScript library created by React Developers from Facebook Inc.

React is pretty mature and time-tested, and it has been used by a lot of household names (Netflix, AirBnB, Instagram). It’s more lightweight and performant than Angular, which is Google’s solution to building front-end applications. React also doesn’t impose any specific ways you have to do things, and its rich ecosystem ensures a wide range of additional libraries to choose from. There is no single “React way”. Thus, you can build your own React stack to suit your particular needs. 

Each app consists of single elements, such as buttons, dropdowns, cards, and infoboxes, which are combined into larger elements, such as menus and forms.

They display certain information and react to your input, making changes to what is displayed. This logical structure is reflected in how you build an app with React.

The basic unit of structure in React is a component — a building block of your app. This can be anything — a message popup, a button, an icon, a passage of text, or a container to hold all of those inside and pass some data to each one. You simply provide React with information on how all the components should look depending on specific parameters (also known as the application “state”) and it takes care of the rest, making appropriate changes to the view according to the changes in state. 

This is called “declarative programming” and you can already see that it feels natural and logical to work this way — it speeds up the development process and facilitates creating even the most complex front-end applications. React is also very performant in making changes to the views because it virtualizes the content of the entire app and makes changes to this virtual “view”, known as Virtual DOM. Then it figures out the best and fastest way to display these changes on the screen. This results in very smooth and performant apps, providing an excellent user experience.

MVC vs Flux

MVC stands for Model View Controller, which is a design pattern used to decouple user-interface (view), data (model), and application logic (controller). This pattern helps achieve separation of concerns. Using the MVC pattern for websites, requests are routed to a Controller that is responsible for working with the Model to perform actions and/or retrieve data. The Controller chooses the View to display and provides it with the Model. The View renders the final page, based on the data in the Model.

Illustration depicting the MVC architecture. The flow follows a triangular shape in one direction with Model sending communicating with the View, the View with the Controller, and the Controller with the Model.

Flux is an application architecture for building user interfaces. It complements React’s composable view components by utilizing a unidirectional data flow. It’s more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.

Flux is based on MVC architecture, but it provides a lot of important changes. The Flux model consists of 4 main components: Dispatcher, Store, Actions, Views (they’re basically React components). Let’s quickly go through all of these to know what we are dealing with.

Dispatcher — used to broadcast payloads to registered callbacks. This is different from generic pub-sub systems in two ways: 

  • Callbacks are not subscribed to particular events. Every payload is dispatched to every registered callback,
  • Callbacks can be deferred wholly or in part until other callbacks have been executed.

Store — manage application state for a particular domain within your application. From a high level, this basically means that per app section, stores manage the data, data retrieval methods, and dispatcher callbacks.

Action — collections of methods that are called within views (or anywhere else for that matter) to send actions to the Dispatcher. Actions are the actual payloads that are delivered via the dispatcher.

View — React components that listen to change events and retrieve Application state from Stores. Then they pass that data down to their child components via props.

Flux Flow described in the diagram. The flow goes: from Actions to Dispatcher to Callbacks to Ttore to Change Events with Store Queries to React Views to User Interactions to Action Creators to Web API Utils to Web API back to Web API Utils and Action Creators, until it goes to Actions, creating a loop.
Table comparison of Flux and the MVC architecture. MVC has: bidirectional data flow, low scalability, is synchronous, data relations are key, and MVC models are usually single objects. Flux has: unidirectional data flow, stores are responsible for all calculations, high scalability, can be asynchronous when needed, events and actions are key, and the stores don't need to model anything and can just contain a custom state related to the app.

React State Management

In React we provide props (which are states for properties) to particular components to assign data so that we can use it to display specific information or perform certain actions. It is crucial to remember that in React we deal with unidirectional data flow (which is Flux based model). 

In many cases, we struggle with passing the props to a higher component in our component tree. Unfortunately, it’s not possible due to the architecture of React. We can only pass the props down the components tree. If we do find ourselves with a need to provide data to a higher component, we’ll have to create context or a global state.

Props vs State

Those are two very important terms in React. Let’s take a deep dive into them to understand what they truly are.

State is the local state of the component which cannot be accessed and modified outside of the component. It’s equivalent to local variables in a function.

Component manages its own state internally, but — besides setting an initial state — has no business fiddling with the state of its children. You could say the state is private.

Let’s take a look at the state example:

import React, {useState} from 'react'

const LandingPage = () => {
	const [name, setName] = useState("My Name");

	return(
		<div>{name}</div>
	)
}

export default LandingPage;

Props, on the other hand, make components reusable by giving components the ability to receive data from their parent component in the form of props. They are equivalent to function parameters.

Think of props as arguments to a function. React components are functions that return JSX (or more generally, something that’s renderable like React elements, like null, a string, etc.). Typically, when you have a piece of code that you’d like to reuse, you can place that code into a function instead of class and use all of its dynamic values as function parameters.

The same is true for a piece of JSX, except instead of calling it like a normal function you use JSX syntax. The “attributes” supplied in the JSX are what are called props and they are placed together in a single object and passed to the Add component function as the first argument. 

In our example, we will pass a quick boolean flag to the Timer component to let it know if the game has started or not.

import React from 'react'
import Timer from '../Timer'
import Cards from '../Cards'

const LandingPage = () => {

	return (
    		<div className="Game">
      			<div className="Game__status">
       				 <span>
          					<Timer isStarted={startTimer} />
        				 </span>
      			</div>
      			<Cards />
    		</div>
	)

}
export default LandingPage;

Components lifecycle

In React we use components to isolate different parts that make our user interface. All of those work independently and use a render function to return React elements in JSX.

Each component goes through three phases: mounting, updating, and unmounting. You can also think of it as our natural life cycle: we are born, we grow, and in the end, we die. React components are created by being mounted onto the DOM, they change or grow through updates, and finally, they can be removed or unmounted from the DOM. These three milestones are referred to as the React component lifecycle.

Note that a React component ​​may or may not go through all the phases.

Diagram depicting the component lifecycle. The Mounting phase consists of Constructor, Render, updating DOM and refs, and it's triggered by componentDidMount. Updating goes through Render, updating the DOM and refs, and it's triggered by componentDidUpdate. Unmounting is triggered by componentWillUnmount.

The diagram above shows the modern lifecycle of React components. React provides a specific lifecycle method so we can manipulate the current state of components.

Stateful and Stateless Components

In React, we can divide components into two different types. It’s important at this point to know what’s the difference between props and state. We already know that every component can have its own props object and generally separated state objects. In this case, we only care about the state. 

Stateful components are components that are involved in the whole state management process of the application. In simple words, every component that has its own state (or uses a global state) is classified as a stateful component.

In the previous versions of React, the stateful component was always a class component. It was created by extending the React.Component class. Today we don’t need to make the component class–based, for we can use the newest approach, which is basically keeping all components functional (simply JavaScript functions instead of classes). 

A stateful component is dependent on its state object and can change its own state. The component re-renders based on changes to its state and may pass down properties of its state to child components as properties on a props object.

Stateless components are also called dummy components. We use them only to present some sort of UI to the user, so they are not processing any calculations, they are just to display some HTML elements.

A stateless component is usually associated with how a concept is presented to the user. It’s similar to a function in the way that it takes an input (props) and returns the output (React element).

React Redux

State management in React is a very crucial part of application development. It manages all data and event handling in the app. Redux is a predictable state container in Javascript apps. Redux helps with state management by storing all the states which need to be changed throughout the app in one place, which we usually call a Store.

Redux is a very powerful tool in developers’ hands, but it also has some disadvantages. As it’s a very complex way of handling state management, it might be overwhelming and used in cases when it’s not really needed. If you don’t follow its rules properly, chances are, your application will break or behave inappropriately, making it troublesome in use. Because of its complexity, many developers avoid Redux altogether.

State Management depicted in a diagram. The flow goes: Actions to Reduce to Store, which subscribes to View, and the View dispatches the Actions. Then it loops.

The above picture depicts what is involved during the state management process. Technically speaking, this is what Redux is all about. 

View, Store, Action, and Reduce are your boilerplates needed to be able to play with Redux. They’re the reason why developers are so apprehensive about using Redux. Unless you primarily work with Angular — then the same can be achieved with the NgRx store.

View — it’s the front-end of the application that reflects any changes made to the app. As an analogy, we can use a storefront of a toy shop. Standing in front of it, you can notice what customers are buying, what merchandise is currently on sale, and what new seasonal decorations are being displayed. Every change is visible to you. This can be called a View and we will use it further to understand the other boilerplates.

Store — it basically keeps all your state management data in one place. In the toy store analogy, Store will be a warehouse storing all the toys.

Action — In Redux, it has to dispatch an action to the Reducer, so that the state management can be done according to the action sent, which will eventually be reflected in the View of our application. Now, we have seen two new things, which we need to understand, and these are: Action and Dispatch. Action will be your thought that comes into your mind when you enter the toy shop with the intent to buy one or several toys. Dispatch will be your voice informing the Shopkeeper on why you have come to the shop.

Reducer — it makes changes to the State object in the Store to be reflected in the View. In our analogy — you have entered the store and said (Dispatched) you want to buy a certain toy (Action). It reached the shopkeeper, who in this scenario is the Reducer. In an answer to your request, the shopkeeper (Reducer) will give you the desired item in exchange for money. Why is the shopkeeper the Reducer, then? From their perspective, you’ve made changes to the store by reducing the number of available items. This will result in the change being reflected in the View (Toy Store). Everyone is able to see the change your action brought.

All these four boilerplates have to be created to be able to handle the state management with Redux. As you can see, this process is quite complex and those who are unfamiliar with it might feel overwhelmed. That’s why it’s prudent to consider our needs twice before including Redux in our app.

React Hooks & Context API

Hooks are a relatively new addition to the React philosophy — they were introduced in React v16.8. But since this approach is quite interesting, many developers prefer it over using older, class–based solutions.

Hooks let you use state and other React features without writing a class, which brings a positive impact on the performance and the readability of the code.

Since Hooks are regular JavaScript functions, you can combine built-in Hooks provided by React with your own “custom Hooks”. This lets you turn complex problems into one-liners and share them across your application. 

At this point you can see that Hooks are really powerful, giving us an opportunity to build our custom, reusable hooks which we can use everywhere in our application.

Custom Hooks are the most appealing part of the Hooks proposal. But in order for custom Hooks to work, React needs to provide functions with a way to declare state and side effects. And that’s exactly what built-in Hooks like useState and useEffect allow us to do. You can learn more about them in the documentation.

But let’s go deeper. What really is a Hook?

A hook is a function that can let you inside a React state and lifecycle features (according to the React Documentation, a hook lets you “hook into” a React state).

If you worked with a function in React before, sometimes you had the need to add some state to it. Before Hooks, you had to convert this function to a class (enabling you to use State and setState()). With Hooks, you can achieve the same result with a functional component.

There are some rules that you need to follow while using React Hooks, for example:

  • Only Call Hooks at the Top Level. You cannot call Hooks inside loops, conditionals, and nested functions. You should call it at the top level of your React function. This allows React to call it in the same order every render.
  • Do not call Hooks from a regular JavaScript function. Always use a React function component or custom Hooks.

Are Hooks really more effective than the classic, class–based approach? There’s no one definitive answer to that question. You can definitely find someone who claims that classes are better, more efficient, and sophisticated. And — they might be even right. It really does depend on circumstances. For example, if you care about writing as little code as possible, the Hooks are your weapon of choice.

Do you like it already? I do! For me, less code is a better option. Either for optimization of the application, but mainly for readability and general accessibility. In my opinion, Hooks are also less complex, which makes them easier to get into, while giving you the power you need to develop a good piece of code.

Why do we compare React Hooks with Redux? There is one crucial functionality called Context API. Let’s take a look at it and compare it to Redux.

React Context API is a way to essentially create global variables that can be passed around in a React app. This is the alternative to “prop drilling”, or passing props from grandparent to parent to child, and so on. Context is often called a simpler, lighter solution to using Redux for state management.

When to use Context? 

Context is designed to share data that can be considered “global” across the tree of React components, such as the current authenticated user, theme, or preferred language. With the Context API, we can avoid passing props through intermediate elements, and directly pass the props to the last child or the child we are targeting.

Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes. All that we have to do is make a variable (its initialState) and give it and the provider an initial value. This initial value depends upon the project which you are making.

Does it sound simpler than Redux? It definitely requires fewer things to do.

The Context API can be really helpful in specific use cases, such as authentication — when you need to check if the user is authenticated in a few unrelated components. Unfortunately, there is one big disadvantage in using Context API: each change of context causes re-rendering of the component, which makes it really easy to fall into the trap of wasted rendering. Therefore, Context shouldn’t be used on the parent with many child components, because it will re–render all of them

The biggest crime you can commit is to use Context on the main App component. Imagine a situation where all components that you have created are re-rendered when something changes in Context. Such a waste of performance!

We use cookies

We use cookies to make sure your website experience is as easy and as personal as possible. By accepting, you’re allowing them to do their job. Change your cookie preferences if you wish to.

Why? To analyze our visitor data, improve our website and show personalized content to our users. We want to give you a better experience of Massive Pixel Creation. Are you fine with this?