Building a Dynamic Color Switcher with React: State, Props, and Controlled Components

Are you interested in learning how to create a color switching app using React? In this guide, I'll walk you through the process. By the end, you'll have a simple app where you can input a color and see it displayed in real-time.

This cool challenge comes from Dave Gray in the video below:

The goal is to create a color switcher which looks something like this:

  1. A display area that becomes the color you supply.

  2. An input field where users can supply an HTML color value (name).

Setting Up the Project

First, start the React project using the create-react-app toolchain. Open your terminal and run:

npx create-react-app palette_presenter

The Main Components

In our project, we'll have two main components:

  • Square.js: Displays the chosen color.

  • Input.js: Allows users to input a color.

These components will be nested in our App.js component, which serves as the entry point of our React application.

App.js:

 import Square from './Square';
 import Input from './Input';

 function App() {
   return (
     <div className='App'>
       <Square></Square>
       <Input></Input>

     </div>
   );
 }

 export default App;

Passing Data with Props

But how do we ensure that the data among these components can work with each other? For this we use props We will pass color value data between the Square and Input components using props.

The Square Component

The Square component will receive colorValue state as a prop and use it to set the backgroundColor of a section element. We will also display the color value in a paragraph element. A default value for the colorValue prop will be provided also, notice that I have given a default value for the prop in case nothing is supplied in the input component.

  •     import React from 'react'
    
        const Square = ({ colorValue='Empty Color Value' }) => {
          return (
    
            <section
                className='square'
                style={{ backgroundColor: colorValue }}
            >
                <p>{colorValue ? colorValue : 'Empty Value'}</p>
            </section>
          )
        }
    
        export default Square
    

The Input Component

The Input component will receive the colorValue state and set it to what the user shall supply.

I use an input form field here to accept the colorValue as input from the user. The Input component will receive the colorValue state and the setColorValue function to update the color.

Input.js:

  •     import React from 'react'
    
        const Input = ({ colorValue, setColorValue }) => {
          return (
            <form onSubmit={(e) => e.preventDefault()}>
                <label htmlFor="colorInput">Add Color Name</label>
                <input 
                    type="text" 
                    id="colorInput"
                    autoFocus
                    placeholder='Add Color Name'
                    required
                    value={colorValue}
                    onChange={(e) => setColorValue(e.target.value)}
                />
    
            </form>
          )
        }
    
        export default Input
    

    For the input element in the form, we have given a few attributes, let's take a look at them.

  • The autofocus ensures that as the user comes to the page he can straight away type the color without the need to take their cursor to the input field,

  • The value attribute is tied to the state using the state variable

  • The onChange has an event handler anonymous function to change the state variable as the user types to the value of whatever is typed in the field.

    Handling an Unexpected Bug

    • If the display section gets dark, the text from the <p> element will disappear. We solve this by adding a button to toggle the text color so the user can switch the colors.

      Solution

      Color Toggle button:

        import React from 'react'
      
        const Input = ({ colorValue, setColorValue, isDarkText, setIsDarkText }) => {
          return (
            <form onSubmit={(e) => e.preventDefault()}>
                <label htmlFor="colorInput">Add Color Name</label>
                <input 
                    type="text" 
                    id="colorInput"
                    autoFocus
                    placeholder='Add Color Name'
                    required
                    value={colorValue}
                    onChange={(e) => setColorValue(e.target.value)}
                />
                <button
                    type='button'
                    onClick={() => setIsDarkText(!isDarkText)}
                >Toggle text color</button>
            </form>
          )
        }
      
        export default Input
      

      We let the user switch the color property of the text inside the section element by toggling it on the click of a button in case the background is dark. For this purpose we define an isDarkText state which is by default made 'true' and is toggled upon the button click

2 key concepts that made the challenge easier:

When it comes to deciding where you should initialize the state variables and state functions, the below mentioned principles can be very useful:

Single Source of Truth:

The state should be kept in the component that requires it most directly or in a common ancestor if multiple components need to access or modify the state. So in my code, the parent component becomes the App.js as both square and input components are nested inside of it.

State Lifting:

If multiple components need to share the same state, you should lift the state up to their nearest common ancestor.

So in accordance with these 2 principles of state lifting and the single state of truth, the Square.js component has the section element whose styling depends on the colorValue and isDarkText states.

The Input.js component has to set the colorValue state on change so it would require setColorValue state function and the toggle button should have access to the isDarkText and the setIsDarkText state entities.

Find the complete Source Code and play with it live!

Live app

Find the completed source code on the below GitHub repo!

ย