Lifting State Up

A component can have child components that need to share the same state. In order to make that work, we recommend raising the state up to the parent component.

To understand how to list state up, we are going to create a temperature calculator that determines whether water would boil or not. It will accept both Cesius and Fahrenheit, converting between them. So, here's are first function:

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water will boil.</p>
  }
  return <p>The water will not boil.</p>
}

The functional component BoilingVerdict will print out whether the temperature would cause water to boil or not. Next we need to create the Calculator component. It will keep track of temperature through this.state.temperature. It will also render the BoilingVerdict component based on the current temperature:

See the Pen Composi Tuts - Lifting State-1 by Robert Biggs (@rbiggs) on CodePen.

A quick FYI for the Americans viewing this. 0° Cesius is the freezing point and 100° Cesius is the boiling point. So, any value less than 100 should show: 'The water would not boil', and any value of 100 or higher should show: 'The water will boil'.

A Second Temperature Input

Now we want to add a second input for Fahrenheit. And we want them both synched, so that a change in the value of one will update the value of the other. In our previous component we had it own state. Now that we are going to have two components this gets complicated. We would wind up with race conditions over whose state wins out when changes are made. To resolve this we make both of them stateless and move the state up to their parent component.

In order for the two inputs to show the correct values, we'll use a funcional component to create them. This is very basic, both will receiver their data as props:

const TemperatureInput = (props) => (
  <fieldset>
    <legend>Enter temperature in {scaleNames[props.scale]}:</legend>
    <input id={props.scale} value={props.temperature} />
  </fieldset>
)

We can use this functional input as a custom tag in our Calculator component. We will also need to give our parent component two methods to convert between Cesius and Fahrenheit: toCelsius and toFahrenheit. We'll also add a handleEvent method to manage the user input on the two form inputs. With all this in mind, here is our updated temperature calculator:

See the Pen Composi Tuts - Lifting State-2 by Robert Biggs (@rbiggs) on CodePen.

Now we have a calculator with two child components sharing the state of their parent. The parent component handles the state of the child components. The child inputs get their values through props. Since the child inputs are dumb, not knowing anything about their parent, we could more easily reuse them elsewhere.

We could have written the TemperatureInput component in such a way that each input had state and used event emitters to inform the parent of their change, which would then have to notify the other child. As you can see from that description, that would be convoluted. Raising the state of the child inputs to their parent component relieves this unnecessary complexity and results in an easier to understand and maintain component.

Data Should Flow Down

Always keep in mind that with components, data should always flow down to their children. The only time you might need some type of event emitter would be where you had two disparate class components that needed to communicate with each other. It can happen. Any pubsub solution from NPM would be sufficient in such a case.