React Fiber разделяет работу на две основные фазы с разными характеристиками:
// Render Phase - может прерываться
function Component() {
const [count, setCount] = useState(0);
console.log('Render phase'); // Может выполниться несколько раз
return <div>{count}</div>;
}
// Commit Phase - выполняется один раз
useEffect(() => {
console.log('Commit phase'); // Выполнится только один раз
}, []);React Fiber — это как умный планировщик задач, который разделяет работу на две фазы для оптимальной производительности! Понимание этих фаз критично для написания эффективных React приложений. 🚀
Это фаза чистых вычислений без побочных эффектов:
function ExpensiveComponent({ data }) {
// Render Phase - чистые вычисления
const processedData = useMemo(() => {
console.log('Processing data...'); // Может выполниться несколько раз
return data.map(item => ({
...item,
processed: true
}));
}, [data]);
// Render Phase - создание виртуального DOM
return (
<div>
{processedData.map(item => (
<Item key={item.id} data={item} />
))}
</div>
);
}function Component() {
// Этот код может выполниться несколько раз
const expensiveValue = calculateSomething();
// React может прервать здесь для обработки более приоритетных задач
return <div>{expensiveValue}</div>;
}// ❌ Неправильно в Render Phase
function BadComponent() {
document.title = 'New Title'; // Побочный эффект!
localStorage.setItem('key', 'value'); // Побочный эффект!
return <div>Content</div>;
}
// ✅ Правильно в Render Phase
function GoodComponent() {
const data = processData(); // Чистое вычисление
return <div>{data}</div>;
}Это фаза применения изменений с побочными эффектами:
function Component() {
const [count, setCount] = useState(0);
// Commit Phase - Layout Effects (синхронно)
useLayoutEffect(() => {
// Выполняется синхронно после DOM мутаций
const element = document.getElementById('counter');
element.style.color = count > 5 ? 'red' : 'black';
}, [count]);
// Commit Phase - Effects (асинхронно)
useEffect(() => {
// Выполняется асинхронно после рендеринга
document.title = `Count: ${count}`;
// Cleanup функция
return () => {
document.title = 'React App';
};
}, [count]);
return <div id="counter">{count}</div>;
}// getSnapshotBeforeUpdate выполняется здесь
class Component extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// Получаем снимок перед изменениями
return document.getElementById('list').scrollTop;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Используем снимок после изменений
if (snapshot !== null) {
document.getElementById('list').scrollTop = snapshot;
}
}
}// Здесь React применяет изменения к DOM
function Component({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}function Component() {
const ref = useRef();
useLayoutEffect(() => {
// Выполняется синхронно после DOM изменений
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 = () => {
// Высокий приоритет - прервёт другие обновления
flushSync(() => {
setUrgent(prev => prev + 1);
});
};
const handleNormalUpdate = () => {
// Обычный приоритет - может быть прерван
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 ловит ошибки в Render Phase
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Обновляет состояние для показа UI ошибки
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Логирование ошибки (Commit Phase)
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Что-то пошло не так.</h1>;
}
return this.props.children;
}
}import { startTransition, useDeferredValue } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
// Render Phase может быть прерван для более важных обновлений
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) => {
// Немедленное обновление input
setQuery(value);
// Отложенное обновление результатов
startTransition(() => {
// Этот код может быть прерван
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" или "update"
console.log('Duration:', actualDuration);
};
return (
<Profiler id="App" onRender={onRenderCallback}>
<ExpensiveComponent />
</Profiler>
);
}❌ Неправильно:
function Component() {
// Побочный эффект в Render Phase
localStorage.setItem('data', 'value');
return <div>Content</div>;
}✅ Правильно:
function Component() {
useEffect(() => {
// Побочный эффект в Commit Phase
localStorage.setItem('data', 'value');
}, []);
return <div>Content</div>;
}Понимание фаз React Fiber критично для производительности:
Используйте это знание для создания быстрых приложений! 🎯