Skip to content

Latest commit

 

History

History
313 lines (234 loc) · 12.9 KB

react-hooks.md

File metadata and controls

313 lines (234 loc) · 12.9 KB

React: Pure Functions and Hooks

Week 5 Keywords and Questions

  • Why use hooks?
  • When to use useState hook - How can I store state and change state using useState?

Prerequisites

Motivation

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

Objectives

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

Specific Things to Learn

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 using useState?
  • When to use useEffect hook - How does a component know when to apply an effect? - How can I use this with state from useState?

Materials

Lesson

What are pure functions?

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>;

Why were hooks introduced?

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.

How do I use hooks?

  1. Read Hooks Overview
  2. 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?

Guided Practice

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.

  1. 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>
  );
};
  1. 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 (...)
  }
  1. 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 (that newTodo item should be appended to items)
  • 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 []), and status should be updated to be Good 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.

Common Mistakes & Misconceptions

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.

Independent Practice (if time permits)

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"

  1. Fork this sandbox.
  2. Look at the App component, but do not modify it. Look at what props are passed to the TitleGenerator component.
  3. Edit the TitleGenerator so that when the phrase input changes, the Harry Potter title changes and is printed below.

Challenge

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).

  1. Fork this sandbox.
  2. Look at the reducer and initialState. The logic for updating state.counter is already implemented.
  3. How will you implement the logic for itemPrice and discount?
  4. How will you implement the logic for totalPrice? What actions should trigger totalPrice updating?

Check for Understanding

  1. Read this article. Key takeaways:
    • Understand useEffect and useState, and useReducer
    • Understand effects with cleanups and skipping effects
  2. Bonus: look at useContext example and walk through it with a partner

See Hooks API doc for more info.

Supplemental Materials

  • 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!