Learning React - useEffect #4 | 青训营笔记

111 阅读2分钟

📝 Notes for Learning React - useEffect #4

  • 😀 Contents are about learning useEffect

  • 🚨 It is written only for myself (It may have no reference value)

  • 🔗 Source code in my GitHub

📚 useEffect

👇🏻 useEffect - Basics

Why do we use useEffect?

  • useEffect is used when we want to set up side-effect that works out side of the component. (It's like connecting to an external system).

Here is the example that we are going to set the page title document.title = `New Messages(${value})` (It is the code that out of React, right?)

import React, { useState, useEffect } from 'react';
// by default runs after every re-render
// cleanup function
// second parameter
const UseEffectBasics = () => {
  const [value, setValue] = useState(0);
  useEffect(() => {
    console.log('call useEffect');
    document.title = `New Messages(${value})`;
  });

  console.log('render component');
  return (
    <>
      <h2>{value}</h2>
      <button className="btn" onClick={() => setValue(value + 1)}>
        click me
      </button>
    </>
  );
  // We can see 2 times log in our dev tool
  // Because of the `<React.StrictMode></React.StrictMode>`
  // Remove the `<App />` out of the `<React.StrictMode></React.StrictMode>`, and we can see only one log in console
};

export default UseEffectBasics;

👇🏻 useEffect - Conditional

We cannot nest the useEffect in conditional state. useEffect has to be in the top level of the code.

import React, { useState, useEffect } from 'react';
const UseEffectBasics = () => {
  const [value, setValue] = useState(0);

  useEffect(() => {
    console.log('call useEffect');
    if (value > 1) {
      document.title = `New Messages(${value})`;
    }
  });

  console.log('render component');
  return (
    <>
      <h2>{value}</h2>
      <button className="btn" onClick={() => setValue(value + 1)}>
        click me
      </button>
    </>
  );
};

export default UseEffectBasics;

👇🏻 useEffect - Dependency List

If you want to run the useEffect on the initial render (for only once, and it won't call useEffect while the value changed which triggered the re-render), just need to add an empty array [] in the second parameter of useEffect.

import React, { useState, useEffect } from 'react';
const UseEffectBasics = () => {
  const [value, setValue] = useState(0);

  // The dependencies list: `value` ⬇️
  // If we change the `value`, it will trigger this `useEffect` one more time.
  useEffect(() => {
    console.log('call useEffect');
    if (value > 1) {
      document.title = `New Messages(${value})`;
    }
  }, [value]);

  // Only call it once when initializing the render
  useEffect(() => {
    console.log('hello useEffect');
  }, []);

  console.log('render component');
  return (
    <>
      <h2>{value}</h2>
      <button className="btn" onClick={() => setValue(value + 1)}>
        click me
      </button>
    </>
  );
};

export default UseEffectBasics;
💥 So, why do we need reactive dependencies?

💡 This is an example from react.dev

  • A list of dependencies including every value from your component used inside of those functions.

  • Sometimes, we need to fetch data while using useEffect. But the data(reactive value) are out of the useEffect function. We can't "choose" the dependencies of our Effect. Every reactive value used by our Effect’s code must be declared as a dependency. Our Effect’s dependency list is determined by the surrounding code:

function ChatRoom({ roomId }) {
  // `roomId` is a reactive value
  const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // `serverUrl` is a reactive value too

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values
    connection.connect();
    return () => connection.disconnect();
  }, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect
  // ...
}

If either serverUrl or roomId change, our Effect will reconnect to the chat using the new values.

👇🏻 useEffect - Cleanup Function

The cleanup function should stop or undo whatever the setup function was doing.

Just like the name implies, the useEffect cleanup is a function in the useEffect Hook that allows us to tidy up our code before our component unmounts. When our code runs and reruns for every render, useEffect also cleans up after itself using the cleanup function.

import React, { useState, useEffect } from 'react';

// cleanup function
// second argument

const UseEffectCleanup = () => {
  const [size, setSize] = useState(window.innerWidth);

  const checkSize = () => {
    setSize(window.innerWidth);
  };

  useEffect(() => {
    window.addEventListener('resize', checkSize);

    // If we don't use cleanup function.
    // We will see bunch of event listeners in 'Elements' of out browser.
    // Cleanup logic should be “symmetrical” to the setup logic (`window.addEventListener` here)
    // , and should stop or undo whatever setup did:
    return () => {
      window.removeEventListener('resize', checkSize);
    };
  });

  return (
    <>
      <h1>Window</h1>
      <h2>{size}</h2>
    </>
  );
};

export default UseEffectCleanup;

👇🏻 useEffect - Fetching Data

What if we put setUsers(users) on getUsers() which is in useEffect ?

Here is a scenario:

import React, { useState, useEffect } from 'react';

const url = 'https://api.github.com/users';

const UseEffectFetchData = () => {
  const [users, setUsers] = useState([]);

  const getUsers = async () => {
    const response = await fetch(url);
    const users = await response.json();
    // What if we put `setUsers(users)` here?
    // Recap:
    // What is `setUsers()` doing?
    // 1. Preserve the value `users`
    // 2. Triggers re-render
    // So:
    // We `setUsers(users)` here, and update the `users` value.
    // It triggers re-render after updating `users`.
    // After re-render, it will call `getUsers()` again in `useEffect()`
    // Finally, we will get an infinite loop
    // 💡 SOLUTION: add an empty array as dependencies list (Only triggers `useEffect` once when rendering at first time)
    setUsers(users);
    console.log(users);
  };

  // `useEffect` cannot be an async function and returns any `Promises`
  useEffect(() => {
    getUsers();
  }, []);

  return (
    <>
      <h3>github users</h3>
    </>
  );
};

export default UseEffectFetchData;

🙅🏻‍♀️ I still have some questions in useEffect hook.

✉️ Contact me anyway if I get somethings wrong here.