DEV Community

Akhil Dhiman
Akhil Dhiman

Posted on

Lifting State Up

You might've heard about Lifting Your State hundreds of times before but have really been unable to comprehend what does it actually mean, neither do I.

So, I looked up the official docs and the docs say- Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor.

Let's understand this with a simple example.

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class Counter extends React.Component {
  state = {
    counter: 0
  };

  decrement = () => {
    this.setState({ counter: this.state.counter - 1 });
  };

  increment = () => {
    this.setState({ counter: this.state.counter + 1 });
  };

  render() {
    return (
      <>
        <button type="button" onClick={this.decrement}>
          -
        </button>
        {this.state.counter}
        <button type="button" onClick={this.increment}>
          +
        </button>
      </>
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <div className="App">
        <Counter />
        <p>You have clicked 0 times!</p>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

First off, we have a Counter component. This component has the state initialized with a value of counter set to zero. Also, we have increment and decrement methods. The output would something look like this.

Check Here

Now, we want "You clicked 0 times" to increment on clicking the button. In order to do that, could we add

clicks: 0

field in Counter like this and pass this data in the state to the App component? I don't see any other solution.

class Counter extends React.Component {
  state = {
    counter: 0,
    clicks: 0
  };

  ////
  ////

  class App extends React.Component {
  render() {
    return (
      <div className="App">
        <Counter />
        <p>You have clicked 0 times!</p> // how do I update this ?
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Well, I don't think that's quite possible.

So, the solution would be lift the state up. Instead of keeping the state in Counter, we move the state to our App component. See here.

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class Counter extends React.Component {
  render() {
    const { increment, counter, decrement } = this.props;

    return (
      <>
        <button type="button" onClick={decrement}>
          -
        </button>
        {counter}
        <button type="button" onClick={increment}>
          +
        </button>
      </>
    );
  }
}

class App extends React.Component {
  state = {
    clicks: 0,
    counter: 0
  };

  decrement = () => {
    this.setState({
      counter: this.state.counter - 1,
      clicks: this.state.clicks + 1
    });
  };

  increment = () => {
    this.setState({
      counter: this.state.counter + 1,
      clicks: this.state.clicks + 1
    });
  };

  render() {
    const { clicks, counter } = this.state;

    return (
      <div className="App">
        <Counter
          increment={this.increment}
          decrement={this.decrement}
          counter={counter}
        />
        <p>You have clicked {clicks} times!</p>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

Check Here

Now, the funny part is, you might've done this unknowingly before or you'd consider this approach to be the general way of doing it. I totally agree with you, but yeah, that's the whole lifting state up fuzz is. Even the docs definition would make some sense now.

Try the CodeSandBox here.

Thank You for reading this article. Follow me on Twitter for more updates.

Top comments (2)

Collapse
 
fjones profile image
FJones

This, in turn, is why Redux is such a common part of React development: While this approach is very sensible for relatively small projects - or when the shared state can be kept contained one or two levels up - it becomes very difficult for large cross-referencing apps.

Personally, I try to keep lifting limited to two levels, and beyond that I start to consider two options:

  • Is my design flawed in some way when it requires deeper nesting? Could I abstract into logical components that take some of this weight off presentational components?
  • Do I need a state store to keep track of all of this information? Which one (and accompanying patterns) should I choose?
Collapse
 
akhildhiman profile image
Akhil Dhiman • Edited

Yes, you're right.

I think using a state store will definitely solve most of your problems. Lifting state up, prop drilling, whatever you say, I've found Redux to be really helpful when it comes to bigger projects. A single source of truth sounds cool.😊😊