loading...
Cover image for Opinionated React: Use State Colocation

Opinionated React: Use State Colocation

farazamiruddin profile image faraz ahmad Updated on ・3 min read

Intro

I’ve been working with React for over four years. During this time, I’ve formed some opinions on how I think applications should be. This is part 4 in the series of such opinionated pieces.

Why

State location helps reduce the complexity of your application. In some cases, it can actually improve performance.

What does colocate your state mean?

Simply put, it means to put your state as close to where it's being used. You should avoid global state unless it's absolutely necessary.

An example

Here's some code that I've come across several times in my career. I would avoid doing this.

import * as React from "react";

interface PersonFormState {
  firstName: string;
  lastName: string;
}

interface PersonFormAction {
  type: "SET_VALUE";
  payload: { [name: string]: string };
}

function personFormReducer(state: PersonFormState, action: PersonFormAction) {
  switch (action.type) {
    case "SET_VALUE": {
      return { ...state, ...action.payload };
    }
    default: {
      return state;
    }
  }
}

export const PersonFormContainer: React.FC = () => {
  const [state, dispatch] = React.useReducer(personFormReducer, {
    firstName: "",
    lastName: ""
  });

  const handleChange = (name: string, value: string) => {
    const updatedValue = { [name]: value };
    return dispatch({ type: "SET_VALUE", payload: updatedValue });
  };

  return <PersonForm values={state} onChange={handleChange} />;
};

interface PersonFormProps {
  values: {
    firstName: string;
    lastName: string;
  };
  onChange: (name: string, value: string) => void;
}

const PersonForm: React.FC<PersonFormProps> = ({ values, onChange }) => {
  return (
    <form>
      <label htmlFor="firstName">First name</label>
      <input
        name="firstName"
        value={values.firstName}
        onChange={event => onChange(event.target.name, event.target.value)}
      />
      <label htmlFor="lastName">Last name</label>
      <input
        name="lastName"
        value={values.lastName}
        onChange={event => onChange(event.target.name, event.target.value)}
      />
    </form>
  );
};

I find this code overcomplicated and hard to follow. It's also good to note that this will perform poorly, because we are causing a re-render of our entire state tree every time an input's value changes.

Use colocation to simplify your code.

Here's how I would write this.

import * as React from "react";

export const PersonForm: React.FC = () => {
  return (
    <form>
      <label htmlFor="firstName">First name</label>
      <PersonInput name="firstName" />
      <label htmlFor="lastName">Last name</label>
      <PersonInput name="lastName" />
    </form>
  );
};

interface PersonInputProps {
  name: string;
}

const PersonInput: React.FC<PersonInputProps> = ({ name }) => {
  const [value, setValue] = React.useState("");

  return (
    <input
      name={name}
      value={value}
      onChange={event => setValue(event.target.value)}
    />
  );
};

I would always start with state colocation and then lift state when needed. Keep things simple!

Q&A

Every post I will answer a question I received on twitter. Here's this week's question.

There are very few things that I consider global. Most state should be colocated where it's being used. Here are a couple things that I consider global:

  • The current user
  • Language settings
  • A theme

Outside of these three, I'm not sure anything else needs to be global. 👍

Wrapping Up

This is the 4th installment in a series of pieces I will be writing. If you enjoyed this, please comment below. What else would you like me to cover? As always, I’m open to feedback and recommendations.

Thanks for reading.

P.S. If you haven’t already, be sure to check out my previous posts in this series:

  1. An Opinionated Guide to React: Folder Structure and File Naming
  2. An Opinionated Guide to React: Component File Structure
  3. Opinionated React: State Management

Posted on by:

farazamiruddin profile

faraz ahmad

@farazamiruddin

I'm a software engineer with a lot of React and startup experience. I write about my opinions on React, using Firebase with React, and lessons from building my 1st startup, Retro

Discussion

markdown guide