React Fiber divides work into two main phases with different characteristics:
// Render Phase - can be interrupted
function Component() {
const [count, setCount] = useState(0);
console.log('Render phase'); // May execute multiple times
return <div>{count}</div>;
}
// Commit Phase - executes once
useEffect(() => {
console.log('Commit phase'); // Will execute only once
}, []);React Fiber is like a smart task scheduler that divides work into two phases for optimal performance! Understanding these phases is critical for writing efficient React applications. 🚀
This is the phase of pure computations without side effects:
function ExpensiveComponent({ data }) {
// Render Phase - pure computations
const processedData = useMemo(() => {
console.log('Processing data...'); // May execute multiple times
return data.map(item => ({
...item,
processed: true
}));
}, [data]);
// Render Phase - virtual DOM creation
return (
<div>
{processedData.map(item => (
<Item key={item.id} data={item} />
))}
</div>
);
}function Component() {
// This code may execute multiple times
const expensiveValue = calculateSomething();
// React may interrupt here to handle higher priority tasks
return <div>{expensiveValue}</div>;
}// ❌ Wrong in Render Phase
function BadComponent() {
document.title = 'New Title'; // Side effect!
localStorage.setItem('key', 'value'); // Side effect!
return <div>Content</div>;
}
// ✅ Correct in Render Phase
function GoodComponent() {
const data = processData(); // Pure computation
return <div>{data}</div>;
}This is the phase of applying changes with side effects:
function Component() {
const [count, setCount] = useState(0);
// Commit Phase - Layout Effects (synchronous)
useLayoutEffect(() => {
// Executes synchronously after DOM mutations
const element = document.getElementById('counter');
element.style.color = count > 5 ? 'red' : 'black';
}, [count]);
// Commit Phase - Effects (asynchronous)
useEffect(() => {
// Executes asynchronously after rendering
document.title = `Count: ${count}`;
// Cleanup function
return () => {
document.title = 'React App';
};
}, [count]);
return <div id="counter">{count}</div>;
}// getSnapshotBeforeUpdate executes here
class Component extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// Get snapshot before changes
return document.getElementById('list').scrollTop;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Use snapshot after changes
if (snapshot !== null) {
document.getElementById('list').scrollTop = snapshot;
}
}
}// Here React applies changes to DOM
function Component({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}function Component() {
const ref = useRef();
useLayoutEffect(() => {
// Executes synchronously after DOM changes
const rect = ref.current.getBoundingClientRect();
console.log('Element dimensions:', rect);
});
return <div ref={ref}>Content</div>;
}function App() {
const [urgent, setUrgent] = useState(0);
const [normal, setNormal] = useState(0);
const handleUrgentUpdate = () => {
// High priority - will interrupt other updates
flushSync(() => {
setUrgent(prev => prev + 1);
});
};
const handleNormalUpdate = () => {
// Normal priority - can be interrupted
setNormal(prev => prev + 1);
};
return (
<div>
<div>Urgent: {urgent}</div>
<div>Normal: {normal}</div>
<button onClick={handleUrgentUpdate}>Urgent</button>
<button onClick={handleNormalUpdate}>Normal</button>
</div>
);
}// Error Boundary catches errors in Render Phase
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Updates state to show error UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Error logging (Commit Phase)
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}import { startTransition, useDeferredValue } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
// Render Phase can be interrupted for more important updates
const results = useMemo(() => {
return searchData(deferredQuery);
}, [deferredQuery]);
return (
<div>
{results.map(result => (
<div key={result.id}>{result.title}</div>
))}
</div>
);
}
function App() {
const [query, setQuery] = useState('');
const handleSearch = (value) => {
// Immediate input update
setQuery(value);
// Deferred results update
startTransition(() => {
// This code can be interrupted
performExpensiveSearch(value);
});
};
return (
<div>
<input onChange={(e) => handleSearch(e.target.value)} />
<SearchResults query={query} />
</div>
);
}import { Profiler } from 'react';
function App() {
const onRenderCallback = (id, phase, actualDuration) => {
console.log('Component:', id);
console.log('Phase:', phase); // "mount" or "update"
console.log('Duration:', actualDuration);
};
return (
<Profiler id="App" onRender={onRenderCallback}>
<ExpensiveComponent />
</Profiler>
);
}❌ Wrong:
function Component() {
// Side effect in Render Phase
localStorage.setItem('data', 'value');
return <div>Content</div>;
}✅ Correct:
function Component() {
useEffect(() => {
// Side effect in Commit Phase
localStorage.setItem('data', 'value');
}, []);
return <div>Content</div>;
}Understanding React Fiber phases is critical for performance:
Use this knowledge to create fast applications! 🎯