1, What is Effect Hook?
Hook is a special function starting with use, which allows the function component to have some features of the calss component. Effect Hook refers to the special function useEffect, which allows function components to perform custom operations after rendering.
useState should be used with caution in useEffect, because it will trigger the component to call useEffect again after rendering, forming an endless loop. Correct method: wrap useState method with conditional statement, define exit condition and avoid dead loop.
2, 3 ways of use
Let the component only monitor the changes of specific data, and then carry out the operation after rendering, ignoring unnecessary operations, which well optimizes the component performance.
1,useEffect(() => { })
There is only one parameter, which is called after each component rendering and before the next rendering.
// 1. Import useEffect; import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 2. Call with the arrow function as its only argument useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
2,useEffect(() => { }, [])
There are two parameters. The second parameter is an empty array ([]). It is called after the component is loaded for the first time and the rendering is completed, and it is called only once.
useEffect(() => { function doSomething() { console.log('hello'); } doSomething(); }, []);
3,useEffect(() => { }, count)
There are two parameters. The second parameter is an array. useEffect is called only when the value in the array changes.
// useEffect is called only when 'props.source' is changed. useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], );
3, Clearing mechanism
When useEffect is called, some operations are performed (such as setting a timer and accessing some network resources). When the component is unloaded, some cleanup operations must be done to prevent memory leakage.
Solution: you only need to return a cleanup function in useEffect. React will call the scavenging function before the component is unloaded.
// Function component: update and clear user login status // Chat API is a hypothetical module that allows us to subscribe to the online status of our friends. import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Return clear function return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
4, What should I do if the dependency value changes frequently?
1. Question:
useEffect does not specify a dependency, which means that useEffect will only run once, and the internally obtained count is always the initial value 0, resulting in the < H1 > {count} < / H1 > value in the page, which is always 1.
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1>; }
2. Imperfect solution
Add count dependency in useEffect. In this way, every time useEffect executes setCount, the count will be changed, and the useEffect will be called again, which can solve the problem. However, this will bring another problem. Every execution of useEffect will clear the timer and reset the timer, which is not what we want.
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); // This effect depends on the 'count' state }, 1000); return () => clearInterval(id); }, [count]); // Add count dependency return <h1>{count}</h1>; }
3. Final solution
The update feature of setState is adopted to let setCount obtain and update count by itself, so that useEffect completely breaks away from the dependence on count and achieves the final ideal effect.
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1>; }
5, The function component implements this in the class component
1,useRef + useEffect
Use useRef and useEffect to implement it. Only do it when you really can't find a better way, because relying on changes makes components more unpredictable.
function Example(props) { // Save the latest props in a ref const latestProps = useRef(props); useEffect(() => { latestProps.current = props; }); useEffect(() => { function tick() { // props read at any time is up to date console.log(latestProps.current); } const id = setInterval(tick, 1000); return () => clearInterval(id); }, []); // This effect is executed only once }
6, Dependent callback function update
1,useCallback + useEffect
function ProductPage({ productId }) { // ✅ Wrap with useCallback to avoid changing with rendering const fetchProduct = useCallback(() => { // ... Does something with productId ... }, [productId]); // ✅ All dependencies of useCallback are specified return <ProductDetails fetchProduct={fetchProduct} />; } function ProductDetails({ fetchProduct }) { useEffect(() => { fetchProduct(); }, [fetchProduct]); // ✅ All dependencies of useEffect are specified // ... }
7, Uselayouteeffect
useEffect will be called and executed after the component is completely rendered. If the executed useEffect involves changes to the visible DOM, it may bring a visual jump to the customer. At this time, uselayouteeffect can be considered. The use method is exactly the same as that of useEffect, but the time of being called is different.