Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

How do I properly update DOM elements in React.js?

I have a countdown component, and which to output a timer on the page. Right now, I'm using .innerHTML to output the timer. However, I feel like thats wrong when it comes to React. Should I be using setState instead? Or perhaps a different approach entirely?

My current approach has been to create a function countDownTimer that takes a date and then counts down to that date. I'm then outputting that by invoking the function <p id="timer-output">{countDownTimer()}</p>

My main issue is how its being added to the DOM:

document.getElementById("timer-output").innerHTML = days + "d " + hours + "h "
            + minutes + "m " + seconds + "s ";

is there a cleaner way?

import React, { useContext } from 'react';
import { MasterContext } from '../contexts/masterContext';

const Timer = () => {
   // endDate => 'Jan 18, 2020 15:37:25'
    const { endDate } = useContext(MasterContext);

    const countDownTimer = () => {
        // Set the date we're counting down to
            var countDownDate = new Date(endDate).getTime();

            // Update the count down every 1 second
            var x = setInterval(function() {

            // Get today's date and time
            var now = new Date().getTime();

            // Find the distance between now and the count down date
            var distance = countDownDate - now;

            // Time calculations for days, hours, minutes and seconds
            var days = Math.floor(distance / (1000 * 60 * 60 * 24));
            var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
            var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
            var seconds = Math.floor((distance % (1000 * 60)) / 1000);

            // Output the result in an element with id="demo"
            document.getElementById("timer-output").innerHTML = days + "d " + hours + "h "
            + minutes + "m " + seconds + "s ";

            // If the count down is over, write some text 
            if (distance < 0) {
                clearInterval(x);
                document.getElementById("timer-output").innerHTML = "EXPIRED";
            }
            }, 1000);
    }


    return ( 
        <section id="timer">
            <p id="timer-output">{countDownTimer()}</p>
        </section>
     );


}

export default Timer;

2 Answers

Steven Parker
Steven Parker
231,269 Points

Perhaps a template literal is what you want:

document.getElementById("timer-output").innerHTML = `${days}d ${hours}h ${minutes}m ${seconds}s`;

While that is valid JavaScript and will work, that kind of defeats the purpose of using React.

Steven Parker
Steven Parker
231,269 Points

Perhaps I misunderstood what you meant by "cleaner". So, perhaps the React-ish version might be:

ReactDOM.render(`${days}d ${hours}h ${minutes}m ${seconds}s`,
                document.getElementById("timer-output"));

A wrote a very simple React timer to show you the concept of how I would implement it using React. Note I'm not that experienced with React yet either, but this looks more 'clean'.

import React, { Component } from "react";
import "./styles.css";

class Clock extends Component {
  constructor(props) {
    super(props);
    this.state = {
      time: null
    };
  }

  componentDidMount() {
    this.intervalID = setInterval(() => this.setTime(), 1000);
  }
  componentWillUnmount() {
    clearInterval(this.intervalID);
  }

  setTime() {
    let time = new Date().toTimeString();
    this.setState({ time });
  }

  render() {
    return <div>{this.state.time}</div>;
  }
}

export default function App() {
  return <Timer />;
}

class Timer extends Component {
  render() {
    return <Clock />;
  }
}