Course: Section

Get Started with Modern React: Step by Step

Episode: Title

S02・V10: Data Flows Down

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

Objectives
  1. To understand that state is local to the component that owns it, and is not accessible to any other component.
  2. We will make it clear that a component can pass its state down as props to its child components.
  3. We will point out that data flows in one direction only, from the top of the component hierarchy downwards.
  4. We will show that different renderings of the same component are independent of each other.
Watch Video
Duration: 6m 38s

Local State

Neither parent nor child components needs to know if a certain component is stateful or stateless. This is why state is often called “local” or “encapsulated”. State is not accessible to any component other than the one that owns and sets it.

A component may choose to pass its state down as props to its child components. To demonstrate this, let’s make some minor changes to our Clock component.

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

function Clock() {
  const initialDate = new Date();
  const [date, setDate] = useState(initialDate);

  const tick = () => setDate(new Date());

  const effect = () => {
    console.log("Effect fired...");
    const timerID = setInterval(tick, 1000);
    console.log("Create timerID:", timerID);
    const cleanup = () => {
      console.log("Remove timerID:", timerID);
      clearInterval(timerID);
    };
    return cleanup;
  };
  useEffect(effect, []);

  return (
    <div>
      <h1>Hello, World!</h1>
      <h2>It is {date.toLocaleTimeString()}.</h2>
    </div>
  );
}

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

We will start by removing the console.log() statements:

  const effect = () => {
    const timerID = setInterval(tick, 1000);
    const cleanup = () => {
      clearInterval(timerID);
    };
    return cleanup;
  };

Now, we will add a component that takes the date as props, and formats it.

Inside Clock, we will render the FormattedDate component under the Hello, World! header, passing in the date state as props:

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

const FormattedDate = props => {  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;};
function Clock() {
  const initialDate = new Date();
  const [date, setDate] = useState(initialDate);

  const tick = () => setDate(new Date());

  const effect = () => {
    const timerID = setInterval(tick, 1000);
    const cleanup = () => {
      clearInterval(timerID);
    };
    return cleanup;
  };
  useEffect(effect, []);

  return (
    <div>
      <h1>Hello, World!</h1>
      <FormattedDate date={date} />    </div>
  );
}

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

Top-Down Data Flow

React has what is commonly called a “top-down” or “unidirectional” data flow.

In our example, the Clock owns and controls its local date state, and passes it down to the FormattedDate component via props. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components “below” them in the tree. If you imagine a component tree as a waterfall of props, each component’s state is like an additional water source that joins it at an arbitrary point but also flows down.

Components are Encapsulated

Different renderings of the same component are independent of each other.

To show that all components are truly isolated, we can create an App component that renders three Clocks.

function Clock() {
  const initialDate = new Date();
  const [date, setDate] = useState(initialDate);

  const tick = () => setDate(new Date());

  const effect = () => {
    const timerID = setInterval(tick, 1000);
    const cleanup = () => {
      clearInterval(timerID);
    };
    return cleanup;
  };
  useEffect(effect, []);

  return (
    <div>
      <h1>Hello, World!</h1>
      <FormattedDate date={date} />
    </div>
  );
}

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

To make it clear that the three Clocks are independent of each other, we will add a timeDifference prop, and pass in different values. “Unix Time Stamps” are expressed as an integer value that is the number of milliseconds since “1 January 1970”. So, we will express the time difference in milliseconds.

We will set the first Clock to be one hour slow, that is, behind 60 minutes, times 60 seconds, times 1000 milliseconds. The second Clock will show the current time, that is, with no offset. The third Clock will be one hour fast:

const App = () => {
  return (
    <div>
      <Clock timeDifference={-60*60*1000} />      <Clock timeDifference={0} />      <Clock timeDifference={60*60*1000} />    </div>
  );
};

Our Clock component now has props. We will now change the definition of the date state to be a Unix time stamp integer instead of a date string. To do that, we use the getTime() method:

function Clock(props) {
  const initialDate = new Date().getTime();  const [date, setDate] = useState(initialDate);

  const tick = () => setDate(new Date().getTime());

And now, we will add the timeDifference to the date, which is passed into the FormattedDate:

  return (
    <div>
      <h1>Hello, World!</h1>
      <FormattedDate date={date + props.timeDifference} />    </div>
  );

Finally, we need to convert the Unix time stamp date in the FormattedDate component to a date string:

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

const FormattedDate = props => {  return <h2>It is {new Date(props.date).toLocaleTimeString()}.</h2>;};
function Clock(props) {
  const initialDate = new Date().getTime();
  const [date, setDate] = useState(initialDate);

  const tick = () => setDate(new Date().getTime());

  const effect = () => {
    const timerID = setInterval(tick, 1000);
    const cleanup = () => {
      clearInterval(timerID);
    };
    return cleanup;
  };
  useEffect(effect, []);

  return (
    <div>
      <h1>Hello, World!</h1>
      <FormattedDate date={date + props.timeDifference} />
    </div>
  );
}

const App = () => {
  return (
    <div>
      <Clock timeDifference={-60*60*1000} />
      <Clock timeDifference={0} />
      <Clock timeDifference={60*60*1000} />
    </div>
  );
};

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

Save the file, and observe three independent Clocks showing times that are one hour different from each other in Chrome.

Picturing the Component Hierarchy

At the top of the component hierarchy, we have the App component. The App component renders three separate Clock components. Each Clock component renders a FormattedDate component.

Component Hierarchy

The top-level App component passes a timeDifference prop into the first Clock component, with a value of minus 1 hour in milliseconds. It passes a timeDifference prop with zero value into the second Clock component. And, it passes a timeDifference prop of plus 1 hour in milliseconds into the third Clock component.

The Clock component has local date state, and passes its date state value plus the timeDifference prop value into the FormattedDate component’s date prop.

So, data is passed via props from the top of the component hierarchy downwards.

Summary

We showed that state is local to the component that owns it, and is not accessible to any other component. We made it clear that a component can pass its state down as props to its child components. We pointed out that data flows in one direction only, from the top of the component hierarchy downwards. And, we showed that different renderings of the same component are independent of each other.

Next Up…

In the next video, we will cover how React handles the user’s click events.