Skip to content

Commit 45fc577

Browse files
committed
custom hooks and dev tools
1 parent 2f554b4 commit 45fc577

File tree

4 files changed

+140
-5
lines changed

4 files changed

+140
-5
lines changed

lessons/04-core-react-concepts/B-hooks.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ const [pizzaSize, setPizzaSize] = useState("medium");
9696
[…]
9797
</select>;
9898

99-
// replace the div surrounding the radio buttons
100-
<div onChange={(e) => setPizzaSize(e.target.value)}>[…]</div>;
99+
// add to all the radio buttons
100+
onChange={(e) => setPizzaSize(e.target.value)}
101101
```
102102

103103
- This is called a hook. Other frameworks like Vue have adopted it as well.
@@ -113,7 +113,14 @@ const [pizzaSize, setPizzaSize] = useState("medium");
113113
- Similar to above. We're using `onChange` and `onBlur` because it makes it more accessible.
114114

115115
> I'm showing you how to do a "controlled form" in that we're using hooks to control each part of the form. In reality, it'd be better to leave these _uncontrolled_ (aka don't set the value) and wrap the whole thing in a form. Then we can listen for submit events and use that event to gather info off the form. This is less code and less burdensome to write. If you have a standard form sort of thing to write, do that as an uncontrolled form. If you need to do dynamic validation, react to a user typing a la typeahead (functionality that provides real-time suggestions), or something of the ilk, then a controlled input is perfect, otherwise stick to uncontrolled.
116-
> Also what's new in React is called a "form action" that is considered unstable. In the future you will just add `<form action="blah">[…]</form>` and a form action will handle the entire form for you. This will then dovetai
116+
> Also what's new in React is called a "form action" that is considered unstable. In the future you will just add `<form action="blah">[…]</form>` and a form action will handle the entire form for you.
117+
118+
Another side note: event bubbling works in React works just like you would expect. In theory you can have mega event handler in React but the lint rules and React's dev tools get noisy about it if you do it that way so I tend to just follow their recommendation.
119+
120+
```javascript
121+
// you could replace the div surrounding the radio buttons and remove all the onChange handlers
122+
<div onChange={(e) => setPizzaSize(e.target.value)}>[…]</div>
123+
```
117124

118125
> 🏁 [Click here to see the state of the project up until now: 04-hooks][step]
119126

lessons/04-core-react-concepts/C-effects.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ async function fetchPizzaTypes() {
4747

4848
// replace the options
4949
{
50-
pizzaTypes.map((pizza) => <option value={pizza.id}>{pizza.name}</option>);
50+
pizzaTypes.map((pizza) => (
51+
<option key={pizza.id} value={pizza.id}>
52+
{pizza.name}
53+
</option>
54+
));
5155
}
5256

5357
// replace <Pizza /> and button at the end
@@ -58,7 +62,8 @@ async function fetchPizzaTypes() {
5862
- the `[]` at the end of the useEffect is where you declare your data dependencies. React wants to know _when_ to run that effect again. You don't give it data dependencies, it assumes any time any hook changes that you should run the effect again. This is bad because that would mean any time setPets gets called it'd re-run render and all the hooks again. See a problem there? It'd run infinitely since requestPets calls setPets.
5963
- You can instead provide which hooks to watch for changes for. In our case, we actually only want it to run once, on creation of the component, and then to not run that effect again. (we'll do searching later via clicking the submit button) You can accomplish this only-run-on-creation by providing an empty array.
6064
- We're using a loading flag to only display data once it's ready. We'll use TanStack Query in a bit to make this code look cleaner. But this is how you do conditional showing/hiding of components in React.
65+
- The `key` portion is an interesting one. When React renders arrays of things, it doesn't know the difference between something is new and something is just being re-ordered in the array (think like changing the sorting of a results list, like price high-to-low and then priced low-to-high). Because of this, if you don't tell React how to handle those situations, it just tears it all down and re-renders everything anew. This can cause unnecessary slowness on devices. This is what key is for. Key tells React "this is a simple identifier of what this component is". If React sees you just moved a key to a different order, it will keep the component tree. So key here is to associate the key to something unique about that component. 99/100 this is a database ID of some variety. _Don't_ use the index of the array as that just isn't right unless the array is literally is never going to change order.
6166

6267
> 🏁 [Click here to see the state of the project up until now: 05-effects][step]
6368
64-
[step]: https://github.com/btholt/citr-v8-project/tree/master/05-effects
69+
[step]: https://github.com/btholt/citr-v9-project/tree/master/05-effects
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
description: "An essential tool in any React developer's toolbox is the official React Dev Tools extension. Brian shows you how to install and use them."
3+
---
4+
5+
React has some really great tools to enhance your developer experience. We'll go over a few of them here.
6+
7+
## `NODE_ENV=development`
8+
9+
React already has a lot of developer conveniences built into it out of the box. What's better is that they automatically strip it out when you compile your code for production.
10+
11+
So how do you get the debugging conveniences then? Well, if you're using Vite.js, it will compile your development server with an environment variable of `NODE_ENV=development` and then when you run `vite build` it will automatically change that to `NODE_ENV=production` which is how all the extra weight gets stripped out.
12+
13+
Why is it important that we strip the debug stuff out? The dev bundle of React is quite a bit bigger and quite a bit slower than the production build. Make sure you're compiling with the correct environmental variables or your users will suffer.
14+
15+
## Strict Mode
16+
17+
React has a new strict mode. If you wrap your app in `<StrictMode></StrictMode>` it will give you additional warnings about things you shouldn't be doing. I'm not teaching you anything that would trip warnings from `StrictMode` but it's good to keep your team in line and not using legacy features or things that will be soon be deprecated.
18+
19+
Be aware that `StrictMode` continually double-renders your components and will run effects twice. It does this catch subtle bugs where your app will change between renders when it's not meant to. It can be helpful, but to be honest, once you learn to write React the correct way you'll nearly never hit that sort of bug.
20+
21+
## Dev Tools
22+
23+
React has wonderful dev tools that the core team maintains. They're available for both Chromium-based browsers and Firefox. They let you do several things like explore your React app like a DOM tree, modify state and props on the fly to test things out, tease out performance problems, and programtically manipulate components. Definitely worth downloading now. [See here][dev-tools] for links.
24+
25+
[dev-tools]: https://react.dev/learn/react-developer-tools
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
One thing that's pretty special about hooks is their composability. You can use hooks to make other hooks! People tend to call these custom hooks. There are even people who go as far to say "never make an API request in a component, always do it in a hook." I don't know if I'm as hardcore as that but I see the logic in it. If you make a custom hook for those sorts of things they become individually testable and do a good job to separate your display of data and your logic to acquire data. I'm more in the camp of make custom hooks for either complicated logic or reusable logic, but for simple cases it's okay to keep things simple.
2+
3+
Okay, so we want to add a "Pizza of the Day" banner at the bottom of our page. This necessitates calling a special API to get the pizza of the day (which should change every day based on your computer's time.) Let's first write the component that's going to use it.
4+
5+
Make a file called PizzaOftheDay.jsx
6+
7+
```javascript
8+
import { usePizzaOfTheDay } from "./usePizzaOfTheDay";
9+
10+
// feel free to change en-US / USD to your locale
11+
const intl = new Intl.NumberFormat("en-US", {
12+
style: "currency",
13+
currency: "USD",
14+
});
15+
16+
const PizzaOfTheDay = () => {
17+
const pizzaOfTheDay = usePizzaOfTheDay();
18+
19+
if (!pizzaOfTheDay) {
20+
return <div>Loading...</div>;
21+
}
22+
23+
return (
24+
<div>
25+
<div className="pizza-of-the-day-info">
26+
<h2>Pizza of the Day</h2>
27+
<h3>{pizzaOfTheDay.name}</h3>
28+
<p>{pizzaOfTheDay.description}</p>
29+
</div>
30+
<img
31+
className="pizza-of-the-day-image"
32+
src={pizzaOfTheDay.image}
33+
alt={pizzaOfTheDay.name}
34+
/>
35+
<p className="pizza-of-the-day-price">
36+
From: <span>{intl.format(pizzaOfTheDay.sizes.S)}</span>
37+
</p>
38+
</div>
39+
);
40+
};
41+
42+
export default PizzaOfTheDay;
43+
```
44+
45+
- The cool part here is the `usePizzaOfTheDay()`. We now just get to rely on that this going to provide us with the pizza of the day from within the black box of the hook working.
46+
47+
Okay, let's go make the hook! Make a file called usePizzaOfTheDay.jsx (you could call this .js instead of jsx but in a React project I just use JSX for all "React-y" things)
48+
49+
```javascript
50+
import { useState, useEffect } from "react";
51+
52+
export const usePizzaOfTheDay = () => {
53+
const [pizzaOfTheDay, setPizzaOfTheDay] = useState(null);
54+
55+
useEffect(() => {
56+
async function fetchPizzaOfTheDay() {
57+
const response = await fetch("/api/pizza-of-the-day");
58+
const data = await response.json();
59+
setPizzaOfTheDay(data);
60+
}
61+
62+
fetchPizzaOfTheDay();
63+
}, []);
64+
65+
return pizzaOfTheDay;
66+
};
67+
```
68+
69+
- This looks like what you'd see at the top of a component, right? Now it's just encapsulated as a hook which makes it easy to test by itself! 🎉
70+
- We can use `useState`, `useEffect`, or any hook we want here! We can even use other custom hooks!
71+
72+
Lastly, let's go add this to App.jsx so our component renders.
73+
74+
```javascript
75+
// import at top
76+
import PizzaOfTheDay from "./PizzaOfTheDay";
77+
78+
// under <Order />
79+
<PizzaOfTheDay />;
80+
```
81+
82+
You should now see the new component which uses our new hook at the bottom!
83+
84+
Let's add one more fun debugging technique made especially for custom hooks. Put this in usePizzaOfTheDay.jsx:
85+
86+
```javascript
87+
// import useDebugValue
88+
import { useState, useEffect, useDebugValue } from "react";
89+
90+
// add under the hook being used in the render function
91+
useDebugValue(pizzaOfTheDay ? `${pizzaOfTheDay.name}` : "Loading...");
92+
```
93+
94+
Now open your React Dev Tools and inspect our PizzaOfTheDay component. You'll see our debug value there. This is helpful when you have _lots_ of custom hooks and in particular lots of reused custom hooks that have differing values. It can help at a glance to discern which hook has which data inside of it.
95+
96+
> 🏁 [Click here to see the state of the project up until now: 05-custom-hooks][step]
97+
98+
[step]: https://github.com/btholt/citr-v9-project/tree/master/05-custom-hooks

0 commit comments

Comments
 (0)