Example: 80-100 minutes
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:
useState
hookuseEffect
hookPure functions
React hooks
useState
hook - How can I store state and change state using useState
?useEffect
hook - How does a component know when to apply an effect? - How can I use this with state from useState
?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?
const 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.
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:
Please fork this template and follow along with this tutorial.
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>
);
};
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 (...)
}
Adding a Todo
items
list should update (that newTodo
item should be appended to items
)newTodo
should be reset to ''
)Clearing Todos
items
should be reset (set to []
), and status
should be updated to be Good Job!
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”
App
component, but do not modify it. Look at what props are passed to the TitleGenerator
component.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).
reducer
and initialState
. The logic for updating state.counter
is already implemented.itemPrice
and discount
?totalPrice
? What actions should trigger totalPrice
updating?useEffect
and useState
, and useReducer
useContext
example and walk through it with a partnerSee Hooks API doc for more info.
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! Using hooks to change background color Intro to React Hooks Intro to useReducer