As React apps grow in complexity, managing shared state between components can become tricky. Oftentimes, several child components may need to reflect the same data in the UI.

The React solution is to lift the state up to a common ancestor component. The parent component can manage the state, and pass it down to the children via props.

Let’s look at how to lift state for easier data sharing:

The Problem with Local State

Imagine we have a <Toolbox> component that contains some <Tool> components:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function Toolbox() {
  return (
    <div>
      <Tool />
      <Tool />
      <Tool />
    </div>
  );
}

function Tool() {
  // Local state for each tool
  const [isActive, setIsActive] = useState(false);
  
  return (
    <button onClick={() => setIsActive(!isActive)}>
      Tool {isActive ? 'Active' : 'Inactive'}
    </button>
  );
}

This works at first, but fails once we need to coordinate the tool state. We want to activate one tool at a time.

The local isActive state in each <Tool> is independent. We need to lift the state up to the parent <Toolbox> which can pass the state down.

Lifting State Up into a Parent Component

First, remove the local isActive state from <Tool>.

Next, create it in the parent <Toolbox> instead:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function Toolbox() {

  const [activeTool, setActiveTool] = useState(null);

  return (
    <div>
       <Tool 
         isActive={activeTool === 1}
         onClick={() => setActiveTool(1)} 
       />  
       <Tool
         isActive={activeTool === 2}
         onClick={() => setActiveTool(2)}
       />
       <Tool
         isActive={activeTool === 3}
         onClick={() => setActiveTool(3)}
       />
    </div>
  );
}

function Tool({isActive, onClick}) {
  return (
    <button onClick={onClick}>
      {isActive ? 'Active' : 'Inactive'}  
    </button>
  );
}

Now the parent <Toolbox> owns the activeTool state, which it passes down to all <Tool> components.

Clicking a tool will update the parent state, which will re-render all three tool components with the updated prop.

Benefits of Lifting State

This pattern has several benefits:

  • Single source of truth - State is synchronized between components
  • Top-down data flow - Parent has full control over state changes
  • Better separation of concerns - State logic is isolated in parent

This avoids problems from duplicating state across child components.

Downsides of Lifting State

Lifting state can also introduce complexity:

  • More props need to be passed down through the tree
  • Parent may become bloated if it manages too much state
  • Can make optimization harder

Evaluate tradeoffs before lifting state too high. Find the optimal owner component for each state.

Summary

  • Lift shared state up to a common parent component
  • Parent component manages state and passes it down through props
  • Avoid state inconsistencies by centralizing control
  • Balance lifting state with complexity costs

Lifting state helps enforce the uni-directional data flow in React. Mastering this pattern unlocks building complex UIs easily composed of small reusable parts.