Translated by Dave Ceddia.

Click “like” and then look, wechat search [Big Move the world] pay attention to this person without dACHang background, but with a positive attitude upward. In this paper, making github.com/qq449245884… Has been included, the article has been categorized, also organized a lot of my documentation, and tutorial materials.

Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.

Imagine this: You have a function component that works really well, and then one day we need to add a lifecycle method to it.

Uh…

At first we might wonder how we can solve this problem, and then it turns out that the usual way to do it is to convert it to a class. But sometimes we just have to do it in a functional way. How do we break it? UseEffect Hook appears to solve this situation.

UseEffect allows you to handle lifecycle events directly within a function component. If you’re familiar with React class lifecycle functions, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount. Here’s an example:

import React, { useEffect, useState } from 'react'; import ReactDOM from 'react-dom'; Function LifecycleDemo() {useEffect() => {// By default, console.log('render! '); For example, if you want to implement componentWillUnmount, // return a function at the end. // React calls this method before the function component is unmounted. // But you can also return an arrow function or give it another name. return function cleanup () { console.log('unmounting... '); } }) return "I'm a lifecycle demo"; } function App() {// create a state, for convenience // trigger the rerender method. const [random, setRandom] = useState(Math.random()); Const [mounted, setMounted] = useState(true); // Create a state for LifecycleDemo to display and hide const [mounted, setMounted] = useState(true); Const reRender = () => setRandom(math.random ()); // LifecycleDemo will be unmounted and remounted. // Unmounting is printed const toggle = () => setMounted(! mounted); return ( <> <button onClick={reRender}>Re-render</button> <button onClick={toggle}>Show/Hide LifecycleDemo</button> {mounted && <LifecycleDemo/>} </> ); } ReactDOM.render(<App/>, document.querySelector('#root'));Copy the code

Try it out in CodeSandbox.

Click the “Show/Hide” button and look at the console. It prints “Unmounting…” before disappearing. And print “Render!” when it appears again. .

Now, click the Re-Render button. Every time YOU click, it will render! , will also print umounting, which seems strange.

Why does every render print ‘unmounting’?

The cleanup function, which we can optionally return from useEffect, is called only when the component is unloaded. React performs cleanup operations when components are uninstalled. As you learned earlier, an effect is executed every time it is rendered. This is why React cleans up the previous effect before executing the current effect. This is actually more powerful than the componentWillUnmount life cycle because it allows us to perform side effects before and after each render if needed.

Incomplete life cycle

UseEffect runs after each rendering (by default) and can optionally clean itself up before running again.

Rather than think of useEffect as a function that does three separate life cycles, it can simply be seen as a way to perform side effects after rendering, including cleanup that we want to perform before each rendering and before unloading.

Prevents useEffect from being executed every time you re-render

If you want Effect to run less, you can provide a second parameter-value array. Think of them as dependencies of the effect. If one of the dependencies has changed since the last time, Effect will run again.

const [value, setValue] = useState('initial'); UseEffect (() => {// Update console.log(value) only when value changes; }, [value])Copy the code

In the example above, we pass [value] as the second argument. What does this parameter do? If value is 5 and our component rerenders with value equal to 5, React will compare the previous render [5] to the last render [5]. Because all elements in the array are equal (5 === 5), React skips this effect, which optimizes performance.

Execute only during mount and unmount

If you want to execute effect once (only when the component is mounted and unmounted), you can pass an empty array ([]) as the second argument. This tells React that your effect doesn’t depend on any value in props or state, so it never needs to be repeated. This is not a special case — it still follows the way dependent arrays work.

useEffect(() => { console.log('mounted'); return () => console.log('unmounting... '); }, [])Copy the code

Mounted is printed only when the component is first rendered and unmounting is printed after the component is unmounted.

However, this hides a problem: passing empty arrays is bug-prone. If we add a dependency, it’s easy to forget to add an item to it, and if we miss a dependency, the value will be invalidated the next time we run useEffect, which can cause some strange problems.

Execute only at mount time

In this example, let’s see how to use useEffect and useRef hook to focus the input control on the first render.

import React, { useEffect, useState, useRef } from "react"; import ReactDOM from "react-dom"; Const inputRef = useRef(); function App() {const inputRef = useRef(); Const [value, setValue] = useState(""); UseEffect (() => {// This will run console.log("render") after the first render; // inputRef.current.focus(); }, // effect depends on inputRef [inputRef]); return ( <input ref={inputRef} value={value} onChange={e => setValue(e.target.value)} /> ); } ReactDOM.render(<App />, document.querySelector("#root"));Copy the code

At the top, we use useRef to create an empty ref. Pass it to the INPUT ref Prop and set it when rendering the DOM. And, importantly, the value returned by useRef is stable between renders – it does not change.

Therefore, even if we pass [inputRef] as the second parameter to useEffect, it actually only runs once during the initial mount. This is basically componentDidMount effect now.

UseEffect to get the data

Let’s look at another common use case: taking data and displaying it. In a class component, you can place this code in the componentDidMount method. You can use useEffect Hook to do this. Of course, you need to use useState to store data.

Below is a component that takes posts from Reddit and displays them

import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom"; function Reddit() { const [posts, setPosts] = useState([]); useEffect(async () => { const res = await fetch( "https://www.reddit.com/r/reactjs.json" ); const json = await res.json(); setPosts(json.data.children.map(c => c.data)); }); // No second argument is passed here, guess what? // Render as usual return ( <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } ReactDOM.render( <Reddit />, document.querySelector("#root") );Copy the code

Notice that we didn’t pass the second argument to useEffect, which is bad, don’t do that.

Not passing the second argument causes useEffect to run on every render. Then, as it runs, it retrieves the data and updates the status. Then, once the state is updated, the component is rerendered, which triggers the useEffect again, and that’s where the problem lies.

To solve this problem, we need to pass an array as the second argument, and what is the contents of the array?

The only variable that useEffect depends on is setPosts. Therefore, we should pass the array [setPosts] here. Because setPosts is the setter returned by useState, it is not recreated every time it is rendered, so effect will only run once.

Retrieve when the data changes

The virtual then extends the example to cover another common problem: how to retrieve data when something changes, such as a user ID, name, and so on.

First, let’s change the Reddit component to accept subreddit as a prop, get data based on that subreddit, and re-run Effect only when the prop changes.

Function Reddit({subreddit}) {const [posts, setPosts] = useState([]); useEffect(async () => { const res = await fetch( `https://www.reddit.com/r/${subreddit}.json` ); const json = await res.json(); setPosts(json.data.children.map(c => c.data)); // Re-run useEffect:}, [subreddit, setPosts]); return ( <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } ReactDOM.render( <Reddit subreddit='reactjs' />, document.querySelector("#root") );Copy the code

This is still hard coded, but now we can customize it by wrapping the Reddit component that allows us to change the subreddit.

function App() { const [inputValue, setValue] = useState("reactjs"); const [subreddit, setSubreddit] = useState(inputValue); // Update the subreddit when the user presses enter const handleSubmit = e => { e.preventDefault(); setSubreddit(inputValue); }; return ( <> <form onSubmit={handleSubmit}> <input value={inputValue} onChange={e => setValue(e.target.value)} /> </form>  <Reddit subreddit={subreddit} /> </> ); } ReactDOM.render(<App />, document.querySelector("#root"));Copy the code

Try this example in CodeSandbox.

The application retains two states here: the current input value and the current subreddit. Submitting the form submits the subreddit, which causes Reddit to retrieve the data again.

By the way: be careful when typing, because there is no error handling, so when the subreddit you typed doesn’t exist, the application will explode, implement error handling as an exercise for you.

You could use just one state to store your input and send the same value to Reddit, but the Reddit component gets the data every time you press a button.

UseState at the top looks a bit strange, especially in the second line:

const [inputValue, setValue] = useState("reactjs");
const [subreddit, setSubreddit] = useState(inputValue);
Copy the code

We pass the initial value of reactJS to the first state, which makes sense, and that value never changes.

What about the second line, what if the initial state changes, like when you type box.

Remember, useState is stateful. It only uses the initial state once, the first rendering, after which it is ignored. So it is safe to pass a transient value, such as a prop that may change or other variables.

Lots and lots of uses

UseEffect is like a Swiss army knife. It can be used for everything from setting up subscriptions to creating and cleaning timers to changing the value of refs.

Unlike componentDidMount and componentDidUpdate, the function passed to useEffect is delayed after the browser has laid out and drawn. This makes it suitable for many common side effect scenarios, such as setting up subscriptions and event handling, so blocking the browser update screen should not be performed in the function.

However, not all effects can be delayed. For example, DOM changes visible to the user must be executed synchronously before the browser performs the next drawing so that the user does not feel visually inconsistent. (Conceptually similar to the difference between passive and active listening events.) React provides an additional useLayoutEffect Hook to handle such effects. It has the same structure as useEffect except when it is called.

UseEffect is guaranteed to be executed before any new rendering, although it is delayed after the browser has drawn. React will refresh the effect of the last render before the component is updated.

The original:Daveceddia.com/useeffect-h…

The bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs, I spent a lot of time on log debugging. Incidentally, I recommend a good BUG monitoring tool for youFundebug.

communication

This article is updated every week, you can search wechat “big move the world” for the first time to read and urge more (one or two earlier than the blog hey), this article GitHub github.com/qq449245884… It has been included and sorted out a lot of my documents. Welcome Star and perfect. You can refer to the examination points for review in the interview.