- Why use hooks?
- When to use useState hook - How can I store state and change state using useState?
React is one of the most popular frameworks for front-end development. It is important to have a way to store and change component state that is efficient and easy to read. Components can have their own state, or use shared stateful logic. Before hooks, there were many different approaches to sharing stateful logic. Hooks are great because they introduce one common way to reuse code. See here for more info
Participants will be able to:
- Understand what pure functions and pure components are
- Understand and use the
useState
hook - Understand and use the
useEffect
hook - Understand the benefits of using hooks
Pure functions
- How are pure functions used in React?
- What is a pure component?
React hooks
- What versions of react support hooks?
- Why use hooks?
- When to use
useState
hook - How can I store state and change state usinguseState
? - When to use
useEffect
hook - How does a component know when to apply an effect? - How can I use this with state fromuseState
?
Look at the functions in the pure functions article (also copied below). Why is multiply
not a pure function? Why is multiplyNumber
a pure function?
let globalNumber = 4;
const multiply = (x) => {
return (globalNumber *= x);
};
const multiplyNumber = (x) => {
return x * 2;
};
Pure components, also called (Stateless)Functional Component, or (S)FC in React, are pure functions - they are components that do not store any internal state, and are pure with respect to their props. For example,
const HeadlineComponent = (props) => <h1>{props.headline}</h1>;
Read Intro to react hooks (5-10 minutes).
Note: React hooks were introduced in version 16.8. Previous alternatives to hooks include render props, higher-order components such as the HOC used to connect a component to the redux store. Right now you don't have to understand all of these alternatives in depth. The main point to note is there were a lot of solutions for the same problem.Hooks introduce one concept that solves these problems.
- Read Hooks Overview
- Walk through the examples on Using the State Hook to understand the state hook and its Class equivalent (in the counter
Example
component ). Use this code sandbox, to play around with these two ways of creating the same functionality.
Discuss with a partner: How do hooks achieve the same logic as the class component? Can you see how the code with hooks is cleaner?
Goal: understand how we would use React hooks to implement a simple to-do list.
A user should be able to:
- Add items (via text input field)
- Press a button 'All done' which will clear all todo items, and a message on the top will say 'Good job!'
- Press a button 'Clear all' which will clear all todo items
- When there are more than 5 items on the list, there should be a status message on the top that says 'Try to complete current tasks before adding new ones! ' This will walk through one possible approach to solving this using React hooks. Note that there is no one right answer or one right approach!
Please fork this template and follow along with this tutorial.
- What should the component look like? We can start with a 'sketch' of what our component might look like. This does not need UI or styling. We start with an outline so that we can think about what information and functionality this component will need.
For example,
const TodoApp = () => {
return (
<div>
<b>status here</b>
<form>
<label>
New item:
<input type="text" name="name" />
</label>
<input type="submit" value="Add" />
<div>
<button> Clear all </button>
<button> All done </button>
</div>
</form>
<h2> My Todos:</h2>
<ul>
<li>item1</li>
<li>item2</li>
</ul>
</div>
);
};
- What information do we need? (aka what states do we need?)
We want to store a list of todos and a status message. We need to store the 'new item' value that the user is typing into the 'Add todo' input field
At the top of the component we can use useState
to do so:
const TodoApp = () => {
const [items, setItems] = useState([])
const [status, setStatus] = useState('')
const [newTodo, setNewTodo] = useState('');
return (...)
}
- When should this information update? We now think about when we want these states to update.
Adding a Todo
- When we type in the input field, we should store that value as 'newTodo`
- When we press the button add a todo item, the
items
list should update (thatnewTodo
item should be appended toitems
) - After we add this item, the input field should clear (
newTodo
should be reset to''
)
Clearing Todos
- When a user presses 'all done',
items
should be reset (set to[]
), andstatus
should be updated to beGood Job!
- When the user presses 'clear all',
items
should be reset (set to[]
)
Too many items
When items.length
is more than 5, the message should update to 'Try to complete current tasks before adding new ones!'
This could look like:
const TodoApp = () => {
const [items, setItems] = useState(['Item one']);
const [status, setStatus] = useState('');
const [newTodo, setNewTodo] = useState('');
return (
<div>
<b>{status}</b>
<form
onSubmit={(e) => {
e.preventDefault();
// We want update items to be old items + a new item
// we do this with setItems
// We also want to reset the input field value
setItems([...items, newTodo]);
setNewTodo('');
}}
>
<label>
New item:
<input
type="text"
name="name"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
</label>
<input type="submit" />
<div>
<button
onClick={(e) => {
e.preventDefault();
setItems([]);
}}
>
Clear all
</button>
<button
onClick={(e) => {
e.preventDefault();
setItems([]);
setStatus('Good job!');
}}
>
All done
</button>
</div>
</form>
<h2> My Todos:</h2>
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</div>
);
};
Are there side effects of any actions? We can use useEffect
for this
Yes! In this example, every time an item is added we want to check the length of items
. If there are more than 5 items, we want to update status
to 'Try to complete current tasks before adding new ones!'
useEffect(() => {
if (items.length > 5) {
setStatus('Try to complete current tasks before adding new ones!');
}
}, [items]);
Note the second argument is an optional array. This means that we only want the effect to run when items
changes. This is because the status does not care about when other states update, such as newTodo
.
Bonus: Notice that after we press 'All done', the 'Good Job!' status will remain until we change it to 'Try to complete current tasks before adding new ones!' (and vise versa). This UI could be confusing for the user. Why would this be confusing, and how could we fix it? Discuss with a partner.
A sample completed component is here.
Infinite loops with useEffect
With no parameters, useEffect
will update after every render (every time the state or props have changed).
In some cases, this can lead to infinite loops. See this example where there is an infinite loop when an API is called in useEffect
.
Calling the API updates the name
, which triggers a re-render, which triggers useEffect
to call the API, leading to an infinite loop.
Unnecessary updating
It is often unnecessary to call useEffect
after every re-render. For example, say you have a Yelp-like component.
There is an API getRestaurants
that you want to call every time the user changes the selected city, city
. We can specify that we only want to call getRestaurants
when the city changes with
useEffect(() => {
getRestaurants(city);
}, [city]);
See here for more info.
Too many states
If a component has multiple states
and/or if the logic to update states becomes complex, it can become difficult to read and bloated.
The useReducer hook.
Book name generator
Goal: A user should be able to enter a phrase, and below will see the Harry Potter title generated from that phrase.
We want the title to be "Harry Potter and the PHRASE_HERE"
.
For example, if I enter 'Goblet of Code' into the Phrase input, I should see "The National best selling book is called: Harry Potter and the Goblet of Code"
- Fork this sandbox.
- Look at the
App
component, but do not modify it. Look at what props are passed to theTitleGenerator
component. - Edit the
TitleGenerator
so that when the phrase input changes, the Harry Potter title changes and is printed below.
useReducer
Goal: make a simple grocery price calculator using the useReducer
hook.
The user can enter an item price, a percentage discount, and select the number of items. They should see below what the final price will be.
For example, if the item price is 10, discount is 50%, and there are 5 items, the final price should be 25 (10 _ .5 _ 5).
- Fork this sandbox.
- Look at the
reducer
andinitialState
. The logic for updatingstate.counter
is already implemented. - How will you implement the logic for
itemPrice
anddiscount
? - How will you implement the logic for
totalPrice
? What actions should triggertotalPrice
updating?
- Read this article.
Key takeaways:
- Understand
useEffect
anduseState
, anduseReducer
- Understand effects with cleanups and skipping effects
- Understand
- Bonus: look at
useContext
example and walk through it with a partner
See Hooks API doc for more info.
- Hooks vs Classes - Another similar article comparing hooks to classes (7-12 minutes)
- Medium article written by members of the React team who created hooks! (10-15 minutes). Includes a video of the team introducing the concept at React Conf.
More examples: Note: There are many examples and tutorials around React and React hooks. Feel free to browse the interwebs to find resources that make sense to you!