≡ Menu

How to Build a Traffic Light System Using React

Building a traffic light component in React is a great way to understand how to manage state and side effects. In this tutorial, we’ll walk through a simple, yet effective, example using the useState and useEffect hooks to create an automatic, cycling traffic light.

Setting Up the Component

 

First, we need to create our component. We’ll call it TrafficLight and import useState and useEffect from React.

import React, { useState, useEffect } from 'react';

const TrafficLight = () => {
  // We'll add our code here
};

Managing State with useState

The core of our traffic light is its current state—which light is active?

We’ll use the useState hook to keep track of this. We’ll initialize the state with 'red', as this is the starting light.

const [activeLight, setActiveLight] = useState('red');
  • activeLight: This variable holds the current active color (‘red’, ‘yellow’, or ‘green’).
  • setActiveLight: This is the function we’ll use to update the activeLight state.

Creating the Visuals

Before we get to the logic, let’s create the visual part of our component. We’ll use some basic CSS-in-JS styling to create the container and the three light circles. The styling is defined in the styles object at the bottom of the component.

The key part here is how we apply the colors. For each light div, we’ll set its backgroundColor dynamically based on the activeLight state. If a light’s color matches the activeLight state, it will be bright; otherwise, it will be a darker, inactive color.

return (
  <div style={styles.container}>
    <div style={{ ...styles.light, backgroundColor: activeLight === 'red' ? 'red' : 'darkred' }}></div>
    <div style={{ ...styles.light, backgroundColor: activeLight === 'yellow' ? 'yellow' : 'goldenrod' }}></div>
    <div style={{ ...styles.light, backgroundColor: activeLight === 'green' ? 'green' : 'darkgreen' }}></div>
  </div>
);

 

Handling the Side Effect with useEffect

 

This is where the magic happens. We want our traffic light to automatically cycle through its states. This “cycling” is a side effect—it’s a process that happens over time, outside of the standard component rendering.

The useEffect hook is perfect for this!

We’ll use setTimeout inside a useEffect to change the activeLight after a certain duration.

useEffect(() => {
  // Logic to cycle the lights will go here
}, [activeLight]);

The second argument to useEffect, [activeLight], is the dependency array.

This tells React to re-run this effect every time the activeLight state changes.

The Cycling Logic

 

Inside useEffect, we’ll set a timer based on the current active light.

  • If the light is ‘red’, we’ll wait for 4000 milliseconds (4 seconds) and then change the state to ‘green’.
  • If the light is ‘green’, we’ll wait for 3000 milliseconds (3 seconds) and then change the state to ‘yellow’.
  • If the light is ‘yellow’, we’ll wait for 500 milliseconds (0.5 seconds) and then change the state back to ‘red’.

 

useEffect(() => {
  const timer = setTimeout(() => {
    if (activeLight === 'red') {
      setActiveLight('green');
    } else if (activeLight === 'green') {
      setActiveLight('yellow');
    } else if (activeLight === 'yellow') {
      setActiveLight('red');
    }
  }, activeLight === 'red' ? 4000 : activeLight === 'green' ? 3000 : 500);

  // Cleanup function
  return () => clearTimeout(timer);
}, [activeLight]);

The line return () => clearTimeout(timer); is crucial.

It’s the cleanup function for useEffect.

Every time activeLight changes and useEffect runs again, this function will be called first.

It clears the previous setTimeout so we don’t have multiple timers running at the same time, which would cause unexpected behavior.


Final Component

Here is the complete code for our TrafficLight component:

import React, { useState, useEffect } from 'react';

const TrafficLight = () => {
  const [activeLight, setActiveLight] = useState('red');

  useEffect(() => {
    const timer = setTimeout(() => {
      if (activeLight === 'red') {
        setActiveLight('green');
      } else if (activeLight === 'green') {
        setActiveLight('yellow');
      } else {
        setActiveLight('red');
      }
    }, activeLight === 'red' ? 4000 : activeLight === 'green' ? 3000 : 500);

    return () => clearTimeout(timer); // Cleanup on unmount
  }, [activeLight]);

  return (
    <div style={styles.container}>
      <div style={{ ...styles.light, backgroundColor: activeLight === 'red' ? 'red' : 'darkred' }}></div>
      <div style={{ ...styles.light, backgroundColor: activeLight === 'yellow' ? 'yellow' : 'goldenrod' }}></div>
      <div style={{ ...styles.light, backgroundColor: activeLight === 'green' ? 'green' : 'darkgreen' }}></div>
    </div>
  );
};

const styles = {
  container: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    padding: '20px',
    backgroundColor: '#333',
    borderRadius: '10px',
  },
  light: {
    width: '80px',
    height: '80px',
    borderRadius: '50%',
    margin: '10px 0',
    boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)',
  },
};

export default TrafficLight;

With this, you have a fully functional, self-cycling traffic light component in React.

You’ve learned how to use useState for state management and useEffect to handle side effects like timers, including the crucial cleanup process.

{ 0 comments… add one }

Leave a Comment