ReactJS - Component Life Cycle
ReactJS - Component Lifecycle with Hooks
Section titled “ReactJS - Component Lifecycle with Hooks”In React, components go through a lifecycle: mounting (being added to the DOM), updating (re-rendering due to prop or state changes), and unmounting (being removed from the DOM). Historically, class components used specific lifecycle methods (e.g., componentDidMount, componentWillUnmount). In modern React, functional components primarily use the useEffect hook to handle side effects and actions related to these lifecycle phases.
The useEffect Hook: Managing Side Effects
Section titled “The useEffect Hook: Managing Side Effects”useEffect allows you to perform side effects in your functional components. Common side effects include data fetching, subscriptions, or manually changing the DOM. It runs after every render by default, but you can control when it runs.
Its basic syntax is useEffect(setupFunction, dependenciesArray?):
_ setupFunction: The function containing the side effect logic. It can optionally return a cleanupFunction.
_ dependenciesArray (optional): An array of values (props or state) that the effect depends on. The effect will only re-run if these values change between renders. If omitted, the effect runs after every render. If an empty array [] is provided, the effect runs only once after the initial render (mount) and the cleanup function runs on unmount.
Simulating Lifecycle Phases with useEffect
Section titled “Simulating Lifecycle Phases with useEffect”- Initialization (like
constructor): In functional components, initialization of state is done withuseState, and refs withuseRef. This happens when the component function is first called. - Mounting (like
componentDidMount): To run an effect only once after the component mounts, provide an empty dependency array[]touseEffect. This is ideal for initial data fetching or setting up subscriptions. - Updating (like
componentDidUpdate): To run an effect when specific props or state change, include those values in the dependency array. The effect will run after the initial render and after any subsequent render where a dependency has changed. - Unmounting (like
componentWillUnmount): If thesetupFunctioninuseEffectreturns another function (thecleanupFunction), React will run this cleanup function before the component unmounts, and also before re-running the effect due to dependency changes. This is crucial for cleaning up resources like subscriptions or timers to prevent memory leaks. componentWillReceiveProps(Deprecated): In functional components, re-renders due to prop changes are handled automatically. If you need to perform a side effect in response to a prop change, useuseEffectwith that prop in its dependency array.componentWillMount&componentWillUpdate(Deprecated): These older class lifecycle methods, which ran before rendering, are deprecated. Most of their use cases are covered by running effects after rendering withuseEffect, or by initializing state directly in the component body.
Optimizing Performance with React.memo
Section titled “Optimizing Performance with React.memo”Similar to PureComponent or shouldComponentUpdate in class components, React.memo is a higher-order component that memoizes your functional component. If your component renders the same result given the same props, React can skip re-rendering the component by reusing the last rendered result. This can be a useful performance optimization for components that render often with the same props.
Example: Lifecycle with useEffect
Section titled “Example: Lifecycle with useEffect”Let’s adapt the original example. App is a parent component that updates a number, and Content is a child component that logs messages during its lifecycle phases using useEffect.
App.jsx
Section titled “App.jsx”import React, { useState, useEffect } from 'react';
// Child Componentfunction Content({ myNumber }) { useEffect(() => { console.log('Content: Component did mount (or myNumber changed for the first time)'); // This effect runs on mount AND when myNumber changes. // If you only want it on mount, the dependency array would be []. // If you want it specifically for myNumber updates after mount, you might need a ref to track if it's the first render. console.log('Content: myNumber is now:', myNumber);
return () => { // This cleanup runs before the effect runs again (if myNumber changes) OR on unmount. console.log('Content: Cleanup before next myNumber update or unmount. Current myNumber:', myNumber); }; }, [myNumber]); // Dependency: effect re-runs if myNumber changes
useEffect(() => { console.log('Content: Effect for MOUNT only'); return () => { console.log('Content: Effect for UNMOUNT only - Component will unmount!'); }; }, []); // Empty dependency array: runs only on mount and unmount
return ( <div> <h3>Received Number: {myNumber}</h3> </div> );}
// Parent Componentfunction App() { const [data, setData] = useState(0); const [showContent, setShowContent] = useState(true);
const setNewNumber = () => { setData(prevData => prevData + 1); };
// This simulates the unmounting from the original example useEffect(() => { if (data > 2) { // For demonstration, unmount after a few increments console.log('App: Preparing to unmount Content component in 2 seconds'); setTimeout(() => { setShowContent(false); console.log('App: Content component unmounted.'); }, 2000); } }, [data]);
return ( <div> <button onClick={setNewNumber}>INCREMENT</button> {showContent ? <Content myNumber={data} /> : <p>Content has been unmounted.</p>} </div> );}
export default App;main.js (or index.js)
Section titled “main.js (or index.js)”import React from 'react';import ReactDOM from 'react-dom/client';import App from './App.jsx';
const container = document.getElementById('app');const root = ReactDOM.createRoot(container);
root.render(<App />);
// The unmounting logic is now handled within App.jsx for this example's purpose.// Original example had: setTimeout(() => { ReactDOM.unmountComponentAtNode(document.getElementById('app')); }, 10000);// With React 18, it would be: setTimeout(() => { root.unmount(); }, 10000);Expected Console Output Behavior:
Section titled “Expected Console Output Behavior:”- Initial Render:
_
Content: Component did mount (or myNumber changed for the first time)_Content: myNumber is now: 0_Content: Effect for MOUNT only - Click INCREMENT (data becomes 1):
_
Content: Cleanup before next myNumber update or unmount. Current myNumber: 0_Content: Component did mount (or myNumber changed for the first time)(Effect re-runs due tomyNumberchange) _Content: myNumber is now: 1 - Click INCREMENT again (data becomes 2, then 3 triggers unmount logic):
Similar logs for
myNumberchange to 2. Whendatabecomes 3: _App: Preparing to unmount Content component in 2 seconds(After 2 seconds,Contentunmounts) _Content: Cleanup before next myNumber update or unmount. Current myNumber: 3_Content: Effect for UNMOUNT only - Component will unmount!_App: Content component unmounted.
Understanding the useEffect dependency array is crucial. It controls when your effects run and clean up, preventing unnecessary operations and bugs. Always ensure your dependency array accurately reflects all values from the component scope that your effect uses.