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:
A display area that becomes the color you supply.
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 variableThe
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!
Find the completed source code on the below GitHub repo!