React: Passing State Into Child Components As A Prop

Passing State to Child Component

We have previously shown that we can update the state of a component. We can also pass a state into a child component as a prop, and then change the display of that child component based on this state. For example in our little location based application that we are building here:

class App extends React.Component {
  // initialize state
  state = { lat: null, long: null, errorMessage: "" };

  componentDidMount() {
    window.navigator.geolocation.getCurrentPosition(
      (position) =>
        this.setState({
          lat: position.coords.latitude,
          long: position.coords.longitude,
        }),
      (err) => {
        this.setState({ errorMessage: err.message });
      }
    );
  }

  // react requires us to define render
  render() {
    // if there is an error message and no position data
    if (this.state.errorMessage && !this.state.lat) {
      return (
        <LatLongCard>
          <div className="description">Error: {this.state.errorMessage}</div>
        </LatLongCard>
      );
      // if there is no error message and there IS position data
    } else if (!this.state.errorMessage && this.state.lat) {
      return (
        <LatLongCard>
          <div className="description">Latitude: {this.state.lat}</div>
          <div className="description">Longitude: {this.state.long}</div>
          <SeasonDisplay lat={this.state.lat} />
        </LatLongCard>
      );
      // if no error message and no position data
    } else {
      return (
        <LatLongCard>
          <div className="description">Loading Location...</div>
        </LatLongCard>
      );
    }
  }
}

We have our SeasonDisplay child component inside of the LatLongCard component. We were using the state to simply display the latitude and longitude when we retrieved them, but now we can see that we have also passed the state of lat as a prop into SeasonDisplay. We can then go to this component and modify what it shows based on this input.

// depending on the month and latitude determine if it is summer or winter
const getSeason = (lat, month) => {
  if (month > 2 && month < 9) {
    return lat > 0 ? "summer" : "winter";
  } else {
    return lat < 0 ? "summer" : "winter";
  }
};

const SeasonDisplay = (props) => {
  const season = getSeason(props.lat, new Date().getMonth());
  return <div> {season} </div>;
};

So we have made a function to decide whether it is summer or winter depending on the month and the latitude that we passed in as a prop. By the way if the getSeason function is confusing please see the docs on the Ternary Operator.

prop showing up in component

Then we can easily modify this use an image or an icon instead of the string like so.

const SeasonDisplay = (props) => {
  const season = getSeason(props.lat, new Date().getMonth());
  const seasonIcon = season === 'winter' ? '❄️':'🌞';
  return <div> Season:{seasonIcon} </div>;
};

component with icon

Extracting Options to Config Objects

There is a way to refactor this component to make it fit with convention and be easier for others to maintain in the future. We can extract the options into an object called seasonConfig and place all the options in that object at the top of the component. Then we can reference and deconstruct that object in the display function.

const seasonConfig = {
  summer: {
    text: "It is summer.",
    emoji: "🌞",
  },
  winter: {
    text: "It is Winter.",
    emoji: "❄️",
  },
};

// depending on the month and latitude determine if it is summer or winter
const getSeason = (lat, month) => {
  if (month > 2 && month < 9) {
    return lat > 0 ? "summer" : "winter";
  } else {
    return lat < 0 ? "summer" : "winter";
  }
};

const SeasonDisplay = (props) => {
  const season = getSeason(props.lat, new Date().getMonth());

  // deconstruct seasonConfig object
  const{text, emoji} = seasonConfig[season]; 

  return (
    <div>
      <div> Icon: {emoji} </div>
      <div> Text: {text} </div>
    </div>
  );
};

updated component