How To Think In React - Deep Dive

Fundamentals of State Management!

Β·

10 min read

How To Think In React - Deep Dive

Hi there, it's Howard from Web Dev Distilled!

Welcome to the next article on my React series. 😎

In this article and the next one, I will talk to you about the React Mindset.

This is the Foundation of all great React developers out there.

Buckle up! and let's go.


Recipe of Professional React Applications

A professional React application requires 2 main components.

  1. Know How to Work with React API.

  2. Know How to THINK in React.

With that being said, we'll discuss in-depth about the second one below.

And of course, more about the first one in my upcoming articles. 😁

πŸ‘‡

From an overall perspective, thinking in React will contain 2 parts.

  1. Thinking about State Management.

  2. Thinking about Components, Composition, and Reusability.

πŸ‘‰ Thinking in React is a whole process that can help us build applications in a more structured and robust way.


The "Thinking In React" Process

Here is the approach I took to build React or front-end applications.

  1. Break the desired UI into components and establish the Component Tree.

  2. Build a STATIC version in React - without any state first.

  3. Think about the State:

    1. When to USE STATE?

    2. Types of State: Local or Global?

    3. Where to place each piece of state

πŸ‘‰ Thinking in State transitions, NOT element mutations.

  1. Establish the DATA FLOW

    1. ONE WAY DATA FLOW (From Parent to Child Component)

    2. Child-to-parent communication (Reverse data flow)

    3. Accessing Global State


When you know HOW TO THINK in REACT, you will be able to answer the below questions πŸ‘€

1️⃣ How to Break up the UI design into components

2️⃣ How to make components Reusable?

3️⃣ How to assemble UI from reusable components?

4️⃣ What pieces of state do you need for interactivity? Where to place state, what component should own each piece of state?

5️⃣ Types of State should you use?

6️⃣ How to make data flow through the application?


Understanding State πŸ€”

This article is all about Fundamentals of State Management. Therefore, let's review some core concepts here.

If you already have a lot of experience with Frontend development in general, feel free to skip to the State Management section!

πŸ‘‰ State is the most important concept in React.

Up until this point, we all know that, props is the Data that comes from OUTSIDE of the Component.

That's the data that the component received from its parent.

BUT WHAT IF the Component ITSELF needs to actually HOLD its own data over time? πŸ€”

WHAT IF we want to make our app INTERACTIVE? Changing UI as the result of an action?

That's where STATE comes into play! 😎


So here is the Definition of State.

πŸ‘‰ is the DATA that the component can HOLD over time, necessary for information that it needs to REMEMBER throughout the app's lifecycle.

πŸ‘‰ is the MEMORY of Component

πŸ‘‰ is a single variable in a component, which could be called "state variable/piece of state".

πŸ‘‰ The most important aspect of State is - Update the Component State triggers React to RE-RENDER the component.

Whenever we update a piece of state in a component, that will trigger React to Re-render the component in the User Interface.

It's gonna create a new Updated View for the Component.

πŸ‘‰ State is how React keeps the UI in sync with Data.

πŸ‘‰ State allows developers to: Update the component view (by rendering) + PERSIST local variables during renders.

πŸ‘‰ React "reacts" to State changes to update the UI accordingly.


More Understanding about Props

πŸ‘‰ is external data, owned by Parent Component

πŸ‘‰ Used to communicate data from parent-child components

πŸ‘‰ can be used to configure how child components look.

πŸ‘‰ Similar to function parameters

πŸ‘‰ It's Read-only.

πŸ‘‰ Receiving new props will trigger the component to re-render. Usually when the parent's state has been updated.


More thought about State

πŸ‘‰ Entire UI is the Representation of the current state of all components.

React application is fundamentally all about changing state over time and correctly displaying that state at all times.

πŸ‘‰ With state we view UI as a Reflection of data changing over time Instead of explicit DOM manipulation.

πŸ‘‰ We describe that reflection of data using state, event handlers and JSX.


When to use State?

πŸ‘‰ Use for any data that the component should keep track of, in other words - "Remember" over time.

This is the data will change at some point. In Vanilla JS, that's a let variable, an [] or {}

πŸ‘‰ Whenever you want something in the component to be dynamic, go ahead and create a piece of state for that thing.

And then UPDATE the State when this "thing" change - AKA: it's Dynamic.

Example: The form Button can be disabled or active. To be more specific, it should be disabled when the form is submitting.

So we create a state variable isSubmitting that tracks whether the form is submitting or not.

On isSubmitting = true we disable the Button, and vice versa.

πŸ‘‰ If you want to change the way a component looks, and data it displays, you'll need to update its state. Usually happens in an even handler function.

πŸ‘‰ For data should NOT TRIGGER component to re-renders, DON'T USE STATE.

Just use a regular variable instead. This is a very common beginner mistake.


Let's get into State Management!

If you just working with just one component, or a few components in a small application. State management will not be the problem at all.

But when your application start growing, or you're working in a team of Frontend developers, that's when things got mess up very quickly, if you don't have a solid progress or method to grow and manage all the states people and yourself created.

You started having states scattered all over places everywhere in your application.

Some people created so many redundant states that could be solved by just regular variables or with a ref

Your application's performance started getting worse, there were so many unnecessary re-renders, just because of bad state management. etc, you named it.

That's why to build professional frontend applications at scale in general or understand how to think in React, we should put State Management into the pedestal.

State management is all about Deciding

πŸ‘‰ WHEN to create pieces of state?

πŸ‘‰ WHAT TYPES of State are necessary?

πŸ‘‰ WHERE to place each piece of state?

=> Giving each piece of state a HOME.

πŸ‘‰ How the DATA FLOWS through the app.


Types of State by Accessibility

  1. Local State

    πŸ‘‰ State needed ONLY by ONE or FEW components.

    πŸ‘‰ State that is defined in a component and ONLY that component and child components have access to it (by passing via props).

    πŸ‘‰ We definitely should start with Local state first.

  2. Global State

    πŸ‘‰ State that MANY COMPONENTS might need.

    πŸ‘‰ Shared state that is accessible to every component in the entire application.

When to create? Where to place them?

πŸ‘‰ If a piece of state is only used by this component, just leave it in the component.

πŸ‘‰ If it also needs to be used by a child component, Pass it to the child component through props.

πŸ‘‰ If a piece of state is used by a few sibling components? So lift the state up to first common parent. (more about this in the section below πŸ‘‡)

πŸ‘‰ If it's not all of the above cases, it's probably Global State. We will get into global state management later in this article.


Types of State by Domain

  1. REMOTE State

    πŸ‘‰ All application data loaded from a REMOTE server (API)

    πŸ‘‰ Usually asynchronous

    πŸ‘‰ Needs re-fetching and updating

  2. UI State

    πŸ‘‰ Everything else

    πŸ‘‰ Application theme, list filter, sorting, form data, etc

    πŸ‘‰ Usually SYNCHRONOUS and Stored directly inside the application.


SHARING STATE WITH SIBLING COMPONENT

In React we have One Way data flow.

Data can only flow down from the Parents to the Child Component.

But CANNOT SIDEWAYS to siblings components.

Therefore, we cannot easily communicate a piece of state from ChildComponentB to ChildComponentA as props. That's just not possible.

So we need a way to share State with components that are sideways or further up in the Component tree.

How can we do that? πŸ€”


Let's get into Lifting State Up!

By lifting State up, we have successfully shared one piece of state with multiple components in different positions in the component tree.

🚨 That's something you will use all the time in React applications. Please get used to this pattern.

Let's talk more about the current scenario.

Here we want to share a commonState between <ChildComponentA/> and <ChildComponentB/>

We applied the Lifting State Up technique by place the commonState to the First common Parent component of <ChildComponentA/> and <ChildComponentB/> and pass commonState down to 2 child components as props.

Yay! πŸ™Œ We solved the first problem.

BUT, if the data can only flows from Parent to Children. HowChildComponentA(Child) can update state in theParentComponent(Parent)?

It turns out, the solution is quite simple. 😁

Here is the "Child to Parent Communication"

AKA "Inverse Data Flow"

Child-to-parent communication means the child component updating parent state (the data "flowing" up the component tree).

Here we passed the setCommonState down to any component that needs to update the state.

So now any time inside the <ChildComponentB /> we can use setCommonState to update the commonState in the <ParentComponent/>


Deriving State

It sounds complicated, but the definition is quite straightforward 😎

Derived State: is State that is Computed from an existing piece of state or from props.

Here we have an example:

const [cart, setCart] = useState([
   { name: "iPhone 10", price: 200 },
   { name: "Apple Watch", price: 150 }, 
]);

const [totalItems, setTotalItems] = useState(2);
const [totalPrice, setTotalPrice] = useState(350);

πŸ‘Ž Here we have three separate pieces of state, even though totalItems and totalPrice depend on cart

πŸ‘Ž There is no need to do so, it's quite problematic.

Because we need to keep them updated together (in sync)

Whenever we update the cart we have to manually update the totalItems and totalPrice otherwise, our states will get out of sync.

The second problem is that all of the information about the total items, and total price is already included inside cart state, we can easily compute them from there.

πŸ‘Ž That will cause another problem, 3 state updates will cause 3 re-renders.

πŸ‘‰ Here is the perfect time to apply Deriving State

const [cart, setCart] = useState([
   { name: "iPhone 10", price: 200 },
   { name: "Apple Watch", price: 150 }, 
]);
// 🟒 βœ…
const totalItems = cart.length;
const totalPrice = cart.reduce((acc, cur) => acc + cur, 0);

πŸ‘‰ Just use regular variables, no need useState

πŸ‘‰ cart state is the single source of truth for all related data

πŸ‘‰ Works because re-rendering component will automatically re-calculate all the derived state.

Of course, we cannot derive state for most cases. But whenever you have a situation like the before example, where one state can easily be computed from another piece of state or props.

You should ALWAYS prefer derived state.


State Placement Options

Where to place state?Available ToolsWhen to use?
Local ComponentuseState, useReducer, or useRefLocal State
Parent ComponentuseState, useReducer , or useRefLifting up State
ContextContextAPI, useState or useReducerGlobal UI State (preferably)
3rd-party libraryRedux, Zustand, SWR, React Query, etcGlobal State (Remote server state or UI)
URLReact RouterGlobal State, passing state between pages
BrowserLocalStorage, session storage, etcStoring data in user's browser.


That's a very long article, but I already took a lot of my time to reflect, distill everything I know, and learn from great teachers and resources out there.

I hope you will gain some helpful knowledge here.

Thanks for reading.

Have a great day! 😎

Reference resources: Jonas Schmedtmann & React Documentation.

Β