React Fundamentals III

Kay Ashaolu

Lets go back to our clock example

  • Remember how we executed the tick() function every second
  • We used the setInterval function provided by the browser JavaScript engine to execute the function every second
  • We forced React to render the element over and over again using the ReactDOM.render function

Tick Function Example

import React from "react";
import ReactDOM from "react-dom";

function tick() {
	const element = (
		<div>
			<h1>Hello, world!</h1>
			<h2>It is {new Date().toLocaleTimeString()}.</h2>
		</div>
	);
	ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
					

This isn't terribly efficient

  • If we had to tell each component when to render, we would run into some problems
  • What if one component's data relates to another components data?
  • What if we want to have more than one Clock on the page?
  • This is why we want to start to encapsulate this clock so that it can be used throughout our application
  • Encapsulation method: create a Clock component i.e. a JavaScript class

Clock React Component

import React from "react";
import ReactDOM from "react-dom";

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
function tick() {
  ReactDOM.render(
    <div>
      <Clock date={new Date()} />
      <Clock date={new Date()} />
    </div>,
    document.getElementById('root')
  );
}
setInterval(tick, 1000);
					 

Note what is happening

  • We moved the render logic of the Clock to its own JavaScript class
  • By doing this, we were able to create two clock instances in the tick() function
  • We are still using ReactDOM.render to forcibly rerender both Clock instances

Note what is happening

  • Even though we do have two "Clock components", they are synchronized
  • This is because each Clock is sourcing its date/time info to {this.props.date.toLocaleTimeString()}
  • That will be the same for every Clock instance

What if you want more

  • What if you wanted to have a clock for a different time zone?
  • Then each clock instance would have to have some unique data about itself (e.g. it's current time, its time zone)
  • What we would want is that each clock has its own state

Clock Example

import React from "react";
import ReactDOM from "react-dom";

class Clock extends React.Component {
  constructor(props) {
    super(props);
    let clock_date = new Date()
    clock_date.setHours(clock_date.getHours() + parseInt(this.props.offset));
    this.state = {date: clock_date};
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()} with a {this.props.offset} offset</h2>
      </div>
    );
  }
}
ReactDOM.render(
  ( <div>
      <Clock offset ="+3"/>
      <Clock offset="-3" />
    </div>
  ),
  document.getElementById('root')
);
					

Introducing state

  • Notice that we have more going on in the constructor
  • We are passing a property "offset" that is being used to adjust the current time
  • We are saving the current time in each Clock's state: now we have a mutable value tied to each clock

Notice Clock isn't ticking

  • We don't have any code that's actually changing the state with key "date"
  • Remember that we were using the setInterval function to change the date every second
  • What we now want to do is set these intervals to be created by the Clock itself
  • Next, we want to utilize React's feature of re-rendering a component when the component's state changes

Lifecycle Methods

  • We want to be able to be efficient with the computing resources we use
  • If we are done with a resource, freeing up that resource becomes very important, especially if you have several components in the same page
  • There are special functions that are executed by React during special times

Lifecycle Methods

  • We want to set up a setInterval timer whenever the Clock is fully created and rendered to the DOM for the first time. This is called mounting
  • We also want to clear said timer whenever a specfic Clock component is removed from the DOM for any reason. This is called "unmounting"
  • componentDidMount() does the first, componentWillUnmount() does the second

Lifecycle Example

import React from "react";
import ReactDOM from "react-dom";

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: this.getCurrentDate()};
  }
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
  getCurrentDate() {
    let new_date = new Date();
    new_date.setHours(new_date.getHours() + parseInt(this.props.offset));
    return new_date;
  }
		

Lifecycle Example

tick() {
 this.setState({
  date: this.getCurrentDate()
 });
}

render() {
 return (
  <div>
   <h1>Hello, world!</h1>
   <h2>It is {this.state.date.toLocaleTimeString()} with a {this.props.offset} offset</h2>
  </div>
 );
 }
}

ReactDOM.render(
( <div>
  <Clock offset ="+3"/>
  <Clock offset="-3" />
 </div>
),
document.getElementById('root')
);
			

Lifecycle Example

  • Note that with a single call to ReactDOM we were able to create Clocks that tick
  • This is because we are utilizing the second way to tell React to rerender a component: changing it's state
  • We do this in the tick() function

Lifecycle Example

  • Note that each clock is creating its own setInterval timer using the componentDidMount() function and clearing the timer with the componentWillUnmount() function
  • Note also that it was able to set a class variable to the timerID (i.e. this.timerId)
  • Since the id of the timer that's executing the tick function is not important to the rendering of the component, we don't need it in the state
  • Try to put only the minimum number of variables in the state since if any of the state variables change it rerenders the component

Lifecycle Example

  • Note that in the tick() function, we set the state using the function this.setState() instead of setting the state directly
  • This is intentional and good practice: outside of the constructor you should change the state of a component using these functions
  • Among other advantages this enables React to know to rerender that component

Questions?