|
| 1 | +# React Side Effects |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +We'll talk about how to use side-effects in our function components with the |
| 6 | +`useEffect` hook, and how to get additional functionality in our components |
| 7 | +beyond just returning JSX elements. |
| 8 | + |
| 9 | +## Objectives |
| 10 | + |
| 11 | +1. Understand side effects in programming |
| 12 | +2. Use the `useEffect` hook to write side effects in components |
| 13 | +3. Control when the side effects run by using a dependencies array with `useEffect` |
| 14 | + |
| 15 | +## Side Effects |
| 16 | + |
| 17 | +When you hear about side effects in terms of pharmaceuticals, they're often not |
| 18 | +something to get too excited about (typically, you'd take some aspirin for its |
| 19 | +_main effect_ of making a headache go away instead of its _side effect_ of nausea). |
| 20 | + |
| 21 | +In terms of _programming_, a side effect is defined as: |
| 22 | + |
| 23 | +> an operation, function or expression is said to have a side effect if it |
| 24 | +> modifies some state variable value(s) outside its local environment, that is |
| 25 | +> to say has an observable effect besides returning a value (the main effect) to |
| 26 | +> the invoker of the operation. -- [Wikipedia on Side Effects][side-effects] |
| 27 | +
|
| 28 | +Put more simply, if we call a function and that function causes change in our |
| 29 | +application _outside of the function itself_, it's considered to have caused a |
| 30 | +_side effect_. Things like making _network requests_, accessing data from a |
| 31 | +database, writing to the file system, etc. are common examples of side effects |
| 32 | +in programming. |
| 33 | + |
| 34 | +In terms of a React component, the _main effect_ of the component is to return |
| 35 | +some JSX. That's been true of all of the components we've been working with! One |
| 36 | +of the first rules we learned about function components is that they take in |
| 37 | +props, and return JSX. |
| 38 | + |
| 39 | +_However_, it's often necessary for a component to perform some _side effects_ |
| 40 | +in addition to its main job of returning JSX. For example, we might want to: |
| 41 | + |
| 42 | +- Fetch some data from an API when a component loads |
| 43 | +- Start or stop a timer |
| 44 | +- Manually change the DOM |
| 45 | +- Get the user's location |
| 46 | + |
| 47 | +In order to handle these kinds of side effects within our components, we'll need |
| 48 | +to use another special **hook** from React: `useEffect`. |
| 49 | + |
| 50 | +## The useEffect Hook |
| 51 | + |
| 52 | +To use the `useEffect` hook, we must first import it: |
| 53 | + |
| 54 | +```js |
| 55 | +import React, { useEffect } from "react"; |
| 56 | +``` |
| 57 | + |
| 58 | +Then, inside our component, we call `useEffect` and pass in a callback function |
| 59 | +to run as a _side effect_: |
| 60 | + |
| 61 | +```js |
| 62 | +function App() { |
| 63 | + useEffect( |
| 64 | + // side effect function |
| 65 | + () => { |
| 66 | + console.log("useEffect called"); |
| 67 | + } |
| 68 | + ); |
| 69 | + |
| 70 | + console.log("Component rendering"); |
| 71 | + |
| 72 | + return ( |
| 73 | + <div> |
| 74 | + <button>Click Me</button> |
| 75 | + <input type="text" placeholder="Type away..." /> |
| 76 | + </div>; |
| 77 | + ) |
| 78 | +} |
| 79 | +``` |
| 80 | + |
| 81 | +If you run the example code now, you'll see the console messages appear in this |
| 82 | +order: |
| 83 | + |
| 84 | +- Component rendering |
| 85 | +- useEffect called |
| 86 | + |
| 87 | +So we are now able to run some extra code as a _side effect_ any time our |
| 88 | +component is rendered. |
| 89 | + |
| 90 | +> By using this Hook, you tell React that your component needs to do something |
| 91 | +> after render. React will remember the function you passed (we’ll refer to it |
| 92 | +> as our “effect”), and call it later after performing the DOM updates. -- |
| 93 | +> [useEffect hook][useeffect-hook] |
| 94 | +
|
| 95 | +Let's add some state into the equation, and see how that interacts with our |
| 96 | +`useEffect` hook. |
| 97 | + |
| 98 | +```js |
| 99 | +function App() { |
| 100 | + const [count, setCount] = useState(0); |
| 101 | + const [text, setText] = useState(""); |
| 102 | + |
| 103 | + useEffect(() => { |
| 104 | + console.log("useEffect called"); |
| 105 | + }); |
| 106 | + |
| 107 | + console.log("Component rendering"); |
| 108 | + |
| 109 | + return ( |
| 110 | + <div> |
| 111 | + <button onClick={() => setCount((count) => count + 1)}> |
| 112 | + I've been clicked {count} times |
| 113 | + </button> |
| 114 | + <input |
| 115 | + type="text" |
| 116 | + placeholder="Type away..." |
| 117 | + value={text} |
| 118 | + onChange={(e) => setText(e.target.value)} |
| 119 | + /> |
| 120 | + </div> |
| 121 | + ); |
| 122 | +} |
| 123 | +``` |
| 124 | +
|
| 125 | +Try clicking the button or typing in the input field to trigger updates in |
| 126 | +state. Every time state is set, we should also see those same two console |
| 127 | +messages in the same order: |
| 128 | +
|
| 129 | +- Component rendering |
| 130 | +- useEffect called |
| 131 | +
|
| 132 | +**By default, `useEffect` will run our side effect function every time the |
| 133 | +component re-renders**. However, sometimes we only want to run our side effect |
| 134 | +once. For example -- imagine we're using the `useEffect` hook to fetch some data |
| 135 | +from an external API (a common use case for `useEffect`). How can we control |
| 136 | +when `useEffect` will run our side effect function? |
| 137 | + |
| 138 | +## useEffect Dependencies |
| 139 | + |
| 140 | +React gives us a way to control when the side effect will run, by passing a |
| 141 | +second argument to `useEffect` of a _dependencies array_. It looks like this: |
| 142 | + |
| 143 | +```js |
| 144 | +useEffect( |
| 145 | + // 1st arg: side effect (callback function) |
| 146 | + () => console.log("useEffect called"), |
| 147 | + // 2nd arg: dependencies array |
| 148 | + [count] |
| 149 | +); |
| 150 | +``` |
| 151 | + |
| 152 | +Update the `useEffect` function as above and try running the code again. Now, |
| 153 | +the side effect will only run when the `count` variable changes. We won't see |
| 154 | +any console messages from `useEffect` when typing in the input -- we'll only see |
| 155 | +them when clicking the button! |
| 156 | + |
| 157 | +We can also pass in an _empty_ array of dependencies as a second argument, like |
| 158 | +this: |
| 159 | + |
| 160 | +```js |
| 161 | +useEffect(() => { |
| 162 | + console.log("useEffect called"); |
| 163 | +}, []); // second argument is an empty array |
| 164 | +``` |
| 165 | +
|
| 166 | +Now, the side effect will only run the _first time_ our component renders! Keep |
| 167 | +this trick in mind -- we'll be using it again soon to work with fetching data. |
| 168 | +
|
| 169 | +## Performing Side Effects |
| 170 | +
|
| 171 | +Running a `console.log` isn't the most interesting side effect, so let's try a |
| 172 | +couple other examples. |
| 173 | +
|
| 174 | +One kind of side effect we can demonstrate here is _updating parts of the |
| 175 | +webpage page outside of the React DOM tree_. React is responsible for all the |
| 176 | +DOM elements rendered by our components, but there are some parts of the webpage |
| 177 | +that live outside of this tree. Take, for instance, the `<title>` of our page -- |
| 178 | +this is what shows up in the browser tab, like this: |
| 179 | +
|
| 180 | + |
| 181 | +
|
| 182 | +Updating this part of the page would be considered a _side effect_, so let's use |
| 183 | +`useEffect` to update it! |
| 184 | +
|
| 185 | +```js |
| 186 | +useEffect(() => { |
| 187 | + document.title = text; |
| 188 | +}, [text]); |
| 189 | +``` |
| 190 | +
|
| 191 | +Here, what we're telling React is: |
| 192 | +
|
| 193 | +"Hey React! When my App component renders, I also want you to update the |
| 194 | +document's title. But you should only do that when the `text` variable changes." |
| 195 | +
|
| 196 | +Let's add another side effect, this time running a `setTimeout` function. We |
| 197 | +want this function to _reset_ the `count` variable back to 0 after 5 seconds. |
| 198 | +Running a `setTimeout` is another example of a side effect, so once again, let's |
| 199 | +use `useEffect`: |
| 200 | +
|
| 201 | +```js |
| 202 | +useEffect(() => { |
| 203 | + setTimeout(() => setCount(0), 5000); |
| 204 | +}, []); |
| 205 | +``` |
| 206 | +
|
| 207 | +With this side effect, we're telling React: |
| 208 | +
|
| 209 | +"Hey React! When my App component renders, I also want you to set this timeout |
| 210 | +function. In 5 seconds, you should update state and set the count back to 0. You |
| 211 | +should only set this timeout function once, I don't want a bunch of timeouts |
| 212 | +running every time my component updates. kthxbye!" |
| 213 | +
|
| 214 | +All together, here's what our updated component looks like: |
| 215 | +
|
| 216 | +```js |
| 217 | +function App() { |
| 218 | + const [count, setCount] = useState(0); |
| 219 | + const [text, setText] = useState(""); |
| 220 | + |
| 221 | + useEffect(() => { |
| 222 | + document.title = text; |
| 223 | + }, [text]); |
| 224 | + |
| 225 | + useEffect(() => { |
| 226 | + setTimeout(() => setCount(0), 5000); |
| 227 | + }, []); |
| 228 | + |
| 229 | + console.log("Component rendering"); |
| 230 | + |
| 231 | + return ( |
| 232 | + <div> |
| 233 | + <button onClick={() => setCount((count) => count + 1)}> |
| 234 | + I've been clicked {count} times |
| 235 | + </button> |
| 236 | + <input |
| 237 | + type="text" |
| 238 | + placeholder="Type away..." |
| 239 | + value={text} |
| 240 | + onChange={(e) => setText(e.target.value)} |
| 241 | + /> |
| 242 | + </div> |
| 243 | + ); |
| 244 | +} |
| 245 | +``` |
| 246 | +
|
| 247 | +Explore this code to familiarize yourself with `useEffect`, and see what changes |
| 248 | +by changing the dependencies array. It's also a good idea to add some console |
| 249 | +messages or put in a debugger to see what exactly when the side effects will |
| 250 | +run. |
| 251 | + |
| 252 | +## Conclusion |
| 253 | + |
| 254 | +So far, we've been working with components solely for rendering to the DOM based |
| 255 | +on JSX, and updating based on changes to state. It's also useful to introduce |
| 256 | +_side effects_ to our components so that we can interact with the world outside |
| 257 | +of the React DOM tree and do things like making network requests or setting |
| 258 | +timers. |
| 259 | + |
| 260 | +In the next lessons, we'll see some more practical uses for `useEffect` and get |
| 261 | +practice handling network requests from our components. |
| 262 | +
|
| 263 | +## Resources |
| 264 | +
|
| 265 | +- [React Docs on useEffect][useeffect-hook] |
| 266 | +- [A Complete Guide to useEffect](https://overreacted.io/a-complete-guide-to-useeffect/) |
| 267 | +
|
| 268 | +[side-effects]: https://en.wikipedia.org/wiki/Side_effect_(computer_science)#:~:text=In%20computer%20science%2C%20an%20operation,the%20invoker%20of%20the%20operation. |
| 269 | +[useeffect-hook]: https://reactjs.org/docs/hooks-effect.html |
0 commit comments