Course: Section

Get Started with Modern React: Step by Step

Episode: Title

S02・V19: Lifting State Up (Part 1)

Date Created: July 17th, 2019
Last Updated: 27 days ago

Objective
  • We will set up two temperature inputs with different scales, in order to demo lifting state up in the next video.
Watch Video
Duration: 6m

Calculator Component

Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor.

Let’s see how this works in action. We will create a temperature calculator that calculates whether water would boil at a given temperature. The Calculator component will keep track of the water’s temperature in local state. It will get an initial value of an empty string.

import React, { useState } from "react";
import ReactDOM from "react-dom";

const Calculator = () => {
  const [temperature, setTemperature] = useState("");
};

ReactDOM.render(
  <Calculator />,
  document.getElementById("root")
);

The Calculator component will return a fieldset element, within which we will place the temperature input. First, we will add a legend. Next, we will add the input element. We will make this input element a controlled component, so we will set its value as the temperature. We will listen to updates to the input value using the onChange attribute. Any changes typed into the input field will update the temperature state.

import React, { useState } from "react";
import ReactDOM from "react-dom";

const Calculator = () => {
  const [temperature, setTemperature] = useState("");

  return (    <fieldset>      <legend>Enter temperature in Celsius:</legend>      <input        value={temperature}        onChange={event => setTemperature(event.target.value)}      />    </fieldset>  );};
ReactDOM.render(
  <Calculator />,
  document.getElementById("root")
);

Under the input element, we want to show if water would boil, given the temperature input. We will provide this with another component, which we will call BoilingVerdict. It will have a prop called celcius, which will take the temperature string value converted into a floating point number. We will use JavaScript’s parseFloat() function to do the conversion.

Now, we will define the BoilingVerdict component. If the temperature equals or exceeds 100 degrees Celcius, then water would boil. Otherwise, the water would not boil.

import React, { useState } from "react";
import ReactDOM from "react-dom";

const BoilingVerdict = props => {  if (props.celsius >= 100) {    return <p>The water would boil.</p>;  }  return <p>The water would not boil.</p>;};
const Calculator = () => {
  const [temperature, setTemperature] = useState("");

  return (
    <fieldset>
      <legend>Enter temperature in Celsius:</legend>
      <input
        value={temperature}
        onChange={event => setTemperature(event.target.value)}
      />
      <BoilingVerdict celsius={parseFloat(temperature)} />    </fieldset>
  );
};

ReactDOM.render(
  <Calculator />,
  document.getElementById("root")
);

Let’s test our Calculator in the browser. Let’s type in 120 degrees Celcius. Our Calculator shows that water would boil.

Adding a Second Input

We have a new requirement for our Calculator app. In addition to a Celcius input, we want to provide a Fahrenheit input, and they should be kept in sync. We can start by extracting a TemperatureInput component from Calculator. We will now move the body of Calculator into TemperatureInput. The Calculator component will now return the TemperatureInput element.

import React, { useState } from "react";
import ReactDOM from "react-dom";

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

const TemperatureInput = props => {  const [temperature, setTemperature] = useState("");

  return (
    <fieldset>
      <legend>Enter temperature in Celsius:</legend>
      <input
        value={temperature}
        onChange={event => setTemperature(event.target.value)}
      />
      <BoilingVerdict celsius={parseFloat(temperature)} />
    </fieldset>
  );
};

const Calculator = props => {  return (    <div>      <TemperatureInput />    </div>  );};
ReactDOM.render(
  <Calculator />,
  document.getElementById("root")
);

Now, we will add a scaleNames object. One key will be c for “Celsius”. The other key will be f for “Fahrenheit”. We can now pass in either key as a value to a new prop for TemperatureInput. We will call this prop scale, and pass in the value c. We will have the Calculator return a second TemperatureInput element, with a scale of f. Now, in the TemperatureInput component, we can update the legend depending on the scale prop value.

import React, { useState } from "react";
import ReactDOM from "react-dom";

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

const scaleNames = {  c: "Celsius",  f: "Fahrenheit"};
const TemperatureInput = props => {
  const [temperature, setTemperature] = useState("");

  return (
    <fieldset>
      <legend>Enter temperature in {scaleNames[props.scale]}:</legend>      <input
        value={temperature}
        onChange={event => setTemperature(event.target.value)}
      />
      <BoilingVerdict celsius={parseFloat(temperature)} />
    </fieldset>
  );
};

const Calculator = props => {
  return (
    <div>
      <TemperatureInput scale="c" />      <TemperatureInput scale="f" />    </div>
  );
};

ReactDOM.render(
  <Calculator />,
  document.getElementById("root")
);

We have two inputs now, but when you enter the temperature in one of them, the other doesn’t update. This contradicts our requirement of keeping both of them in sync. In addition, we cannot display the BoilingVerdict from Calculator. The Calculator does not know the current temperature because it is hidden inside the TemperatureInput component.

We will fix these issues in the next video.

Summary

We set up two temperature inputs with different scales, in order to demo lifting state up in the next video.

Next Up…

In the next video, we will continue with our discussion of “lifting state up”, and get our two temperature inputs to sync up with each other.