Unraveling the Mysteries of React’s Component Lifecycle
A New Era of Development
The introduction of Hooks in React 16.8 marked a significant shift in the way developers write components. By leveraging Hooks, developers can save time and avoid unnecessary code and boilerplate.
Understanding the Component Lifecycle
To truly master React, it’s essential to grasp the intricacies of the component lifecycle. React is reactive to changes in props and state, which can trigger re-renders. But what exactly happens when these changes occur?
The Basics of Props and State
Let’s start with the fundamentals. A prop is an external variable passed to a component, while a state is an internal variable that persists across multiple renders. Both props and state variables cause a re-render when they change. The only difference between a state and a ref is that a ref change does not cause a re-render.
const MyComponent = ({ prop }) => {
const [state, setState] = useState(0);
const ref = useRef(null);
// prop change triggers re-render
// state change triggers re-render
// ref change does not trigger re-render
}
Re-Renders: The Unexpected Consequences
Consider a scenario where a parent component changes, triggering a re-render of its child components. You might expect that only the components with changed props or state would re-render. However, in practice, all child components will re-render, even if their props and state remain unchanged.
const Parent = () => {
const [count, setCount] = useState(0);
return (
<div>
<Child />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
const Child = () => {
// re-renders even though props and state remain unchanged
return <p>Hello World!</p>;
}
The Power of Memoization
React provides a built-in function called memo
that can prevent unnecessary re-renders by memoizing the result of the component. This technique should be used sparingly, as it can have unintended consequences.
import { memo } from 'eact';
const MyComponent = memo(() => {
// memoized component
return <p>Hello World!</p>;
});
Call Order: The Bottom-Up Approach
When it comes to useEffect
Hooks, the call order is crucial. Effects are called in a bottom-up fashion, resolving first in the children and then in the parent. This means that the effects will run after the component is mounted, but before the DOM is updated.
const Parent = () => {
useEffect(() => {
// runs last
}, []);
return (
<div>
<Child />
</div>
);
};
const Child = () => {
useEffect(() => {
// runs first
}, []);
return <p>Hello World!</p>;
};
Callback Refs: A Word of Caution
Callback refs can have unexpected behavior, especially when the component is updated. They are called between components rendering and effects running, but can be executed twice, with the ref being null during the first execution.
const MyComponent = () => {
const ref = useRef(null);
useEffect(() => {
// called twice, ref is null during first execution
ref.current && doSomething();
}, [ref]);
return <div ref={ref}></div>;
};
Mastering the Component Lifecycle
React Hooks are powerful, but mastering the component lifecycle requires patience and practice. By understanding the intricacies of re-renders, memoization, and call order, you can optimize your application’s performance and avoid common pitfalls.