Skip to content

Rename updater functions of hooks #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: gh-pages-src
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions lessons/effects.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ So rather than just having `dog` be the static animal, let's make that dynamic a
```javascript
// replace effect
useEffect(() => {
updateBreeds([]);
updateBreed("");
setBreeds([]);
setBreed("");
pet.breeds(animal).then(({ breeds }) => {
const breedStrings = breeds.map(({ name }) => name);
updateBreeds(breedStrings);
setBreeds(breedStrings);
}, console.error);
}, [animal]);
```

- Due to JavaScript closures (the fact that state is preserved for various render function calls) we're able to reference updateBreeds from the outer scope. We use this to update the breed after the successful call to the petfinder API.
- Due to JavaScript closures (the fact that state is preserved for various render function calls) we're able to reference setBreeds from the outer scope. We use this to update the breed after the successful call to the petfinder API.
- The array at the end is peculiar but essential. By default, effects will run at the end of every re-render. This is problematic for us because we're updating breeds, which causes a re-render, which causes another effect, which causes another re-render, etc. What you can to prevent this spiral is give it an array of variables as a second parameter. Now this effect will only happen if one of those variables changes. In this case, it will only cause the effect if `animal` changes. Which is exactly what we want.
- Effects are always called after the first render no matter what.
- We have to pull the strings out of the objects from the API since the dropdown expect a list of strings, hence the map which does just that.
Expand All @@ -66,17 +66,17 @@ Whenever a user selects a new animal, we need to programmatically update the bre

```javascript
// update return
return [state, Dropdown, updateState];
return [state, Dropdown, setState];
```

Now users can optionally programatically accept that function to update their components. Let's use this in the component. In SearchParams.js

```javascript
// replace BreedDropdown declaration
const [breed, BreedDropdown, updateBreed] = useDropdown("Breed", "", breeds);
const [breed, BreedDropdown, setBreed] = useDropdown("Breed", "", breeds);

// first line of the function inside useEffect
updateBreed("");
setBreed("");
```

Now it updates the breed to empty whenever you change animal since you can't have a poodle cat (as cool as that sounds).
Expand Down
26 changes: 13 additions & 13 deletions lessons/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ So if we type in our input and it re-renders, what gets out in the `input` tag?
import React, { useState } from "react";

// replace location
const [location, updateLocation] = useState("Seattle, WA");
const [location, setLocation] = useState("Seattle, WA");

// replace input
<input
id="location"
value={location}
placeholder="Location"
onChange={e => updateLocation(e.target.value)}
onChange={e => setLocation(e.target.value)}
/>;
```

Expand All @@ -75,7 +75,7 @@ const [location, updateLocation] = useState("Seattle, WA");
- Because the previous point is so absolutely critical, the React team has provided us with a lint rule that help us not fall into that trap. That lint rule relies on us, the developers, to follow the convention of calling our hooks `useXxxxxx`. If you're willing to do that, the lint rules will guard you from calling the hooks out of order.
- The argument given to `useState` is the default value. In our case, we gave it `"Seattle, WA"` as our default value.
- `useState` returns to us an array with two things in it: the current value of that state and a function to update that function. We're using a feature of JavaScript called destructuring to get both of those things out of the array.
- We use the `updateLocation` function in the `onChange` attribute of the input. Every time the input is typed into, it's going to call that function which calls `updateLocation` with what has been typed into the input. When `updateLocation` is called, React knows that its state has been modified and kicks off a re-render.
- We use the `setLocation` function in the `onChange` attribute of the input. Every time the input is typed into, it's going to call that function which calls `setLocation` with what has been typed into the input. When `setLocation` is called, React knows that its state has been modified and kicks off a re-render.
- You can make your own custom hooks; `useState` is just one of many.
- Historically, React has been written using `class`es with state being on the instance of the component. This is still a supported pattern in React. We'll see how to do it later.

Expand Down Expand Up @@ -103,16 +103,16 @@ Run `npm install @frontendmasters/pet`.
import { ANIMALS } from "@frontendmasters/pet";

// under location
const [animal, updateAnimal] = useState("");
const [animal, setAnimal] = useState("");

// under the location label
<label htmlFor="animal">
Animal
<select
id="animal"
value={animal}
onChange={e => updateAnimal(e.target.value)}
onBlur={e => updateAnimal(e.target.value)}
onChange={e => setAnimal(e.target.value)}
onBlur={e => setAnimal(e.target.value)}
>
<option />
{ANIMALS.map(animal => (
Expand All @@ -131,8 +131,8 @@ Let's make a third dropdown so you can select a breed as well as an animal.

```javascript
// under your other state inside the component
const [breed, updateBreed] = useState("");
const [breeds, updateBreeds] = useState([]);
const [breed, setBreed] = useState("");
const [breeds, setBreeds] = useState([]);

// under the animal label
<label htmlFor="breed">
Expand All @@ -141,8 +141,8 @@ const [breeds, updateBreeds] = useState([]);
disabled={!breeds.length}
id="breed"
value={breed}
onChange={e => updateBreed(e.target.value)}
onBlur={e => updateBreed(e.target.value)}
onChange={e => setBreed(e.target.value)}
onBlur={e => setBreed(e.target.value)}
>
<option />
{breeds.map(breed => (
Expand All @@ -164,16 +164,16 @@ Make a new file called `useDropdown.js`. Noticed we prefixed it with `use` becau
import React, { useState } from "react";

const useDropdown = (label, defaultState, options) => {
const [state, updateState] = useState(defaultState);
const [state, setState] = useState(defaultState);
const id = `use-dropdown-${label.replace(" ", "").toLowerCase()}`;
const Dropdown = () => (
<label htmlFor={id}>
{label}
<select
id={id}
value={state}
onChange={e => updateState(e.target.value)}
onBlur={e => updateState(e.target.value)}
onChange={e => setState(e.target.value)}
onBlur={e => setState(e.target.value)}
disabled={!options.length}
>
<option />
Expand Down
2 changes: 1 addition & 1 deletion lessons/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ const SearchParams: FunctionComponent<RouteComponentProps> = () => {

// replace useState calls
const [pets, setPets] = useState([] as Animal[]);
const [breeds, updateBreeds] = useState([] as string[]);
const [breeds, setBreeds] = useState([] as string[]);
```

- Always need to be defensive about undefined errors. This is one of the benefits of TypeScript, even if it's a bit annoying.
Expand Down