In this practice you will learn to use one of the most basic hooks, the
useState
hook. You will use the useState
hook to change a background theme
from light to dark, as well as to increment and decrement a number.
Click the Download Project
button at the bottom of this page to go to the
starter repo, then load the repo into CodeSandbox.
Take a look at the UseState.css file in the src/components/UseState
directory. Notice that there is a class for light
and for dark
. You will use
these classes to change your background based on a button click.
Open the index.js file in the UseState directory.
Import the UseState.css file into your index.js file using a relative path.
import './UseState.css';
To create state in your component, you will also need to import the useState
hook from the react
package. Place this import above your CSS import.
import { useState } from 'react';
Inside your functional component, type console.log(useState('light'))
. In
your sandbox browser DevTools, look at the console. Notice the two values that
are returned in the array. The first value is your current state; the second
value is the updater function used to update the state. Now remove the
console.log
.
Next, inside your functional component, declare state by destructuring the two
values returned from the invoked useState
function. Give this useState
function an initial state of 'light'
.
const [theme, setTheme] = useState('light');
Look at the React DevTools in your sandbox browser. Click on the UseState
component. Notice under hooks
-> State
you will see the string light
. This
is the default value you stored in state.
You are now going to add this light theme to your JSX. The outermost div
element should have a className
of "state"
. Add the theme
as another
className
to this div
. The div
should now look like this:
<div className={`state ${theme}`}>
When you look at your browser you should see the light theme color as your background.
If you are doing this project on CodeSandbox, then the first test spec in the 01-theme.test.js when clicking on the Tests tab should now pass.
If you are doing this project locally and you run npm test __tests__/01
, the
first test spec in the 01-theme.test.js should now pass.
You've used the useState
hook to create a background color, but you have not
yet used the updater function. Now you are going to create buttons that will
allow the theme to either be light or dark.
Beneath the h1
tag, the button
element with the text of Dark
should turn
the theme 'dark'
.
Inside the button you will use your first event handler. Create an onClick
handler as an attribute to your button element. Remember that onClick
handlers
in React use camelCase and should be assigned a function. Give the onClick
handler an anonymous function that returns the setTheme
updater function
invoked with the string argument 'dark'
.
In the browser, click the button. Your background should now switch from light to dark.
The first two specs in 01-theme.test.js should now pass.
Repeat the same process for the Light
button which should setTheme
to
'light'
.
All three specs in 01-theme.test.js should now pass.
When you want to store information for a different concern in state, you simply
add an additional state variable to the component using another useState
call.
You now need a state variable to keep track of an ongoing number counter. Add
a component state variable called count
with setCount
as its updater
function. Use the useState
hook. Set the initial value of the count
state
variable to the number 0
.
Check your React DevTools to see that you now have state variable with the
initial value of "light"
and a state variable with the initial value of 0
.
You also want to see a 0
as the count in your browser. Replace
DISPLAY COUNT HERE
in the h2
tag with the value of the count
variable.
Remember to use curly braces!
If you are doing this project on CodeSandbox, then the first test spec in the 02-counter.test.js when clicking on the Tests tab should now pass.
If you are doing this project locally and you run npm test __tests__/02
, the
first test spec in the 02-counter.test.js should now pass.
Add an onClick
event listener to the Increment
button that calls the updater
function for the count
to increment the count
by 1
(turns 0
into 1
,
or 1
into 2
, etc.).
Test this button in your browser.
The first two specs in 02-counter.test.js should now pass.
Add an onClick
event listener to the Decrement
button that will decrement
the count
by 1 (turns 0
into -1
, or -1
into -2
, etc.) using the same
technique.
All three specs in 02-counter.test.js should now pass.
An updater function returned by useState
can take a callback function as an
argument.
// without callback function:
setCount(10); // sets the count to 10
// with callback function:
setCount(() => 10); // still sets the count to 10
The updater function will pass the previous state value that it updates as an
argument to the callback when invoking it. You are now going to use this feature
with the setCount
function to ensure that the state is updated based on the
actual previous state.
Inside your Increment
button setCount
invocation, remove the count + 1
and
replace it with (prevCount) => prevCount + 1
. Do the same for the Decrement
button (with the appropriate adjustment!). While you will not see a difference
here, you are now ensuring that your changes based on a previous state will
update correctly.
Using a callback to update state whenever the update depends on the previous
state is advisable because state updates are handled asynchronously and can be
bundled together. In other words, without the callback, you cannot be sure that
the value stored in count
when the update is invoked will always represent the
most current value.
// without callback function:
// increments the previous count value by 1
setCount(count + 1);
// works but not best practice
// with callback function:
// still increments the previous count value by 1
setCount(prevCount => prevCount + 1);
// This is best practice when using the value of
// the previous state to update the state.
All three specs in 02-counter.test.js should still pass!
Congratulations! In this practice you have learned how to
- Create
state
using theuseState
hook - Update
state
using the returned updater function - Create a separate slice of state for each concern
- Use the
onClick
event listener to execute some functionality when a button is clicked - Use the optional callback function to ensure that the current state is based on the previous state
Create a "Toggle Theme" button to replace the "Light" and "Dark" buttons. This
button should conditionally turn the theme
"light" if the theme
is "dark"
and should turn the theme
"dark" if the theme
is "light". Kind of like a
light switch.
If you are doing this project on CodeSandbox, then the test spec in the 03-toggle-theme.test.js when clicking on the Tests tab should now pass.
If you are doing this project locally and you run npm test __tests__/03
, the
test spec in the 03-toggle-theme.test.js should now pass.