📝 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?
useEffectis 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 dependenciesincluding 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 theuseEffectfunction. We can't "choose" the dependencies of our Effect. Everyreactive valueused 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.