Условный рендеринг в React — это обычный JavaScript: if/else, тернарный оператор, &&. Никакого специального синтаксиса (в отличие от Vue v-if).
function LoginButton({ isLoggedIn }) {
return (
<div>
{isLoggedIn
? <button>Выйти</button>
: <button>Войти</button>
}
</div>
)
}function Notifications({ count }) {
return (
<div>
<span>Уведомления</span>
{count > 0 && <span className="badge">{count}</span>}
{/* Если count === 0, ничего не рендерится */}
</div>
)
}Ловушка с &&: если левая часть 0 (число), React рендерит ноль!
// БАГИ:
{items.length && <List items={items} />} // Если length = 0, рендерит "0"!
// ПРАВИЛЬНО:
{items.length > 0 && <List items={items} />} // явное boolean
{!!items.length && <List items={items} />} // двойное отрицаниеfunction Alert({ type, message }) {
let icon
if (type === 'error') icon = <ErrorIcon />
else if (type === 'warning') icon = <WarningIcon />
else icon = <InfoIcon />
return (
<div className={`alert alert-${type}`}>
{icon}
<span>{message}</span>
</div>
)
}Самый читаемый способ для сложных условий:
function UserProfile({ user, isLoading, error }) {
if (isLoading) return <Spinner />
if (error) return <ErrorMessage text={error} />
if (!user) return <p>Пользователь не найден</p>
// Основной рендер — только если всё хорошо
return (
<div className="profile">
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
}function OrderStatus({ status }) {
switch (status) {
case 'pending': return <Badge color="yellow">Ожидает</Badge>
case 'shipping': return <Badge color="blue">Доставляется</Badge>
case 'delivered': return <Badge color="green">Доставлено</Badge>
case 'cancelled': return <Badge color="red">Отменён</Badge>
default: return <Badge color="gray">Неизвестно</Badge>
}
}Любой компонент с асинхронными данными должен обрабатывать три состояния:
function DataList({ data, isLoading, error }) {
// 1. Загрузка
if (isLoading) {
return <div className="spinner">Загрузка...</div>
}
// 2. Ошибка
if (error) {
return <div className="error">Ошибка: {error.message}</div>
}
// 3. Пустые данные
if (!data || data.length === 0) {
return <div className="empty">Список пуст</div>
}
// 4. Данные есть
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
)
}// Discriminated union — безопасное представление состояний
type AsyncState<T> =
| { status: 'loading' }
| { status: 'error'; error: string }
| { status: 'success'; data: T }
| { status: 'empty' }
function DataDisplay({ state }: { state: AsyncState<User[]> }) {
switch (state.status) {
case 'loading': return <Spinner />
case 'error': return <Error message={state.error} />
case 'empty': return <Empty text="Нет данных" />
case 'success': return <UserList users={state.data} />
}
}TypeScript проверит что все случаи обработаны (exhaustive check).
<!-- Vue — директивы v-if / v-else / v-show -->
<div v-if="isLoading">Загрузка...</div>
<div v-else-if="error">Ошибка</div>
<div v-else>{{ data }}</div>// React — чистый JavaScript
{isLoading ? <div>Загрузка...</div>
: error ? <div>Ошибка</div>
: <div>{data}</div>}Vue v-show (скрывает через CSS, не удаляет DOM) в React реализуется через style={{ display: isVisible ? 'block' : 'none' }} или CSS-класс.
Компонент с четырьмя состояниями: загрузка, ошибка, пустые данные, данные — три подхода к условному рендерингу
// Реализуем компонент DataDisplay с четырьмя состояниями
// на чистом JS, демонстрируя три паттерна условного рендеринга
// ============================================================
// Подход 1: if/else — самый читаемый для сложной логики
// ============================================================
function DataDisplay_IfElse(state) {
// Аналог React:
// if (state.status === 'loading') return <Spinner />
// if (state.status === 'error') return <Error message={state.error} />
// if (state.status === 'empty') return <Empty />
// return <DataList data={state.data} />
if (state.status === 'loading') {
return { type: 'spinner', props: { text: 'Загрузка...' } }
}
if (state.status === 'error') {
return { type: 'error', props: { message: state.error } }
}
if (state.status === 'empty') {
return { type: 'empty', props: { text: 'Список пуст' } }
}
return { type: 'data-list', props: { items: state.data } }
}
// ============================================================
// Подход 2: тернарный оператор — компактно для двух состояний
// ============================================================
function StatusBadge(status) {
// React JSX:
// return (
// <span className={status === 'active' ? 'badge-green' : 'badge-red'}>
// {status === 'active' ? 'Активен' : 'Неактивен'}
// </span>
// )
const isActive = status === 'active'
return {
type: 'span',
props: {
className: isActive ? 'badge-green' : 'badge-red',
children: isActive ? 'Активен' : 'Неактивен'
}
}
}
// ============================================================
// Подход 3: && оператор — показать или ничего
// ============================================================
function NotificationPanel(count) {
// React JSX:
// return (
// <div>
// <h2>Уведомления</h2>
// {count > 0 && <span className="badge">{count}</span>}
// {count > 0 && <button>Очистить все</button>}
// </div>
// )
// Ловушка: count && <badge> — если count=0, рендерит "0"!
// Правильно: count > 0 && <badge>
const badgeEl = count > 0 ? { type: 'span', props: { children: count } } : null
const clearBtn = count > 0 ? { type: 'button', props: { children: 'Очистить все' } } : null
return {
type: 'div',
props: { children: [
{ type: 'h2', props: { children: 'Уведомления' } },
badgeEl, // null — React пропустит
clearBtn // null — React пропустит
].filter(Boolean) } // убираем null
}
}
// ============================================================
// Демонстрация всех состояний
// ============================================================
const states = [
{ status: 'loading' },
{ status: 'error', error: 'Сеть недоступна' },
{ status: 'empty' },
{ status: 'success', data: [{ id: 1, name: 'React' }, { id: 2, name: 'Vue' }] },
]
console.log('=== DataDisplay с разными состояниями ===')
states.forEach(state => {
const result = DataDisplay_IfElse(state)
console.log(`[${state.status}] -> type: ${result.type}`)
})
console.log('\n=== StatusBadge ===')
console.log(StatusBadge('active').props.className) // 'badge-green'
console.log(StatusBadge('inactive').props.children) // 'Неактивен'
console.log('\n=== NotificationPanel ===')
console.log(NotificationPanel(5).props.children.length) // 3 (h2 + badge + button)
console.log(NotificationPanel(0).props.children.length) // 1 (только h2)
// Если бы использовали 0 && badge — получили бы "0" в DOM!Условный рендеринг в React — это обычный JavaScript: if/else, тернарный оператор, &&. Никакого специального синтаксиса (в отличие от Vue v-if).
function LoginButton({ isLoggedIn }) {
return (
<div>
{isLoggedIn
? <button>Выйти</button>
: <button>Войти</button>
}
</div>
)
}function Notifications({ count }) {
return (
<div>
<span>Уведомления</span>
{count > 0 && <span className="badge">{count}</span>}
{/* Если count === 0, ничего не рендерится */}
</div>
)
}Ловушка с &&: если левая часть 0 (число), React рендерит ноль!
// БАГИ:
{items.length && <List items={items} />} // Если length = 0, рендерит "0"!
// ПРАВИЛЬНО:
{items.length > 0 && <List items={items} />} // явное boolean
{!!items.length && <List items={items} />} // двойное отрицаниеfunction Alert({ type, message }) {
let icon
if (type === 'error') icon = <ErrorIcon />
else if (type === 'warning') icon = <WarningIcon />
else icon = <InfoIcon />
return (
<div className={`alert alert-${type}`}>
{icon}
<span>{message}</span>
</div>
)
}Самый читаемый способ для сложных условий:
function UserProfile({ user, isLoading, error }) {
if (isLoading) return <Spinner />
if (error) return <ErrorMessage text={error} />
if (!user) return <p>Пользователь не найден</p>
// Основной рендер — только если всё хорошо
return (
<div className="profile">
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
}function OrderStatus({ status }) {
switch (status) {
case 'pending': return <Badge color="yellow">Ожидает</Badge>
case 'shipping': return <Badge color="blue">Доставляется</Badge>
case 'delivered': return <Badge color="green">Доставлено</Badge>
case 'cancelled': return <Badge color="red">Отменён</Badge>
default: return <Badge color="gray">Неизвестно</Badge>
}
}Любой компонент с асинхронными данными должен обрабатывать три состояния:
function DataList({ data, isLoading, error }) {
// 1. Загрузка
if (isLoading) {
return <div className="spinner">Загрузка...</div>
}
// 2. Ошибка
if (error) {
return <div className="error">Ошибка: {error.message}</div>
}
// 3. Пустые данные
if (!data || data.length === 0) {
return <div className="empty">Список пуст</div>
}
// 4. Данные есть
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
)
}// Discriminated union — безопасное представление состояний
type AsyncState<T> =
| { status: 'loading' }
| { status: 'error'; error: string }
| { status: 'success'; data: T }
| { status: 'empty' }
function DataDisplay({ state }: { state: AsyncState<User[]> }) {
switch (state.status) {
case 'loading': return <Spinner />
case 'error': return <Error message={state.error} />
case 'empty': return <Empty text="Нет данных" />
case 'success': return <UserList users={state.data} />
}
}TypeScript проверит что все случаи обработаны (exhaustive check).
<!-- Vue — директивы v-if / v-else / v-show -->
<div v-if="isLoading">Загрузка...</div>
<div v-else-if="error">Ошибка</div>
<div v-else>{{ data }}</div>// React — чистый JavaScript
{isLoading ? <div>Загрузка...</div>
: error ? <div>Ошибка</div>
: <div>{data}</div>}Vue v-show (скрывает через CSS, не удаляет DOM) в React реализуется через style={{ display: isVisible ? 'block' : 'none' }} или CSS-класс.
Компонент с четырьмя состояниями: загрузка, ошибка, пустые данные, данные — три подхода к условному рендерингу
// Реализуем компонент DataDisplay с четырьмя состояниями
// на чистом JS, демонстрируя три паттерна условного рендеринга
// ============================================================
// Подход 1: if/else — самый читаемый для сложной логики
// ============================================================
function DataDisplay_IfElse(state) {
// Аналог React:
// if (state.status === 'loading') return <Spinner />
// if (state.status === 'error') return <Error message={state.error} />
// if (state.status === 'empty') return <Empty />
// return <DataList data={state.data} />
if (state.status === 'loading') {
return { type: 'spinner', props: { text: 'Загрузка...' } }
}
if (state.status === 'error') {
return { type: 'error', props: { message: state.error } }
}
if (state.status === 'empty') {
return { type: 'empty', props: { text: 'Список пуст' } }
}
return { type: 'data-list', props: { items: state.data } }
}
// ============================================================
// Подход 2: тернарный оператор — компактно для двух состояний
// ============================================================
function StatusBadge(status) {
// React JSX:
// return (
// <span className={status === 'active' ? 'badge-green' : 'badge-red'}>
// {status === 'active' ? 'Активен' : 'Неактивен'}
// </span>
// )
const isActive = status === 'active'
return {
type: 'span',
props: {
className: isActive ? 'badge-green' : 'badge-red',
children: isActive ? 'Активен' : 'Неактивен'
}
}
}
// ============================================================
// Подход 3: && оператор — показать или ничего
// ============================================================
function NotificationPanel(count) {
// React JSX:
// return (
// <div>
// <h2>Уведомления</h2>
// {count > 0 && <span className="badge">{count}</span>}
// {count > 0 && <button>Очистить все</button>}
// </div>
// )
// Ловушка: count && <badge> — если count=0, рендерит "0"!
// Правильно: count > 0 && <badge>
const badgeEl = count > 0 ? { type: 'span', props: { children: count } } : null
const clearBtn = count > 0 ? { type: 'button', props: { children: 'Очистить все' } } : null
return {
type: 'div',
props: { children: [
{ type: 'h2', props: { children: 'Уведомления' } },
badgeEl, // null — React пропустит
clearBtn // null — React пропустит
].filter(Boolean) } // убираем null
}
}
// ============================================================
// Демонстрация всех состояний
// ============================================================
const states = [
{ status: 'loading' },
{ status: 'error', error: 'Сеть недоступна' },
{ status: 'empty' },
{ status: 'success', data: [{ id: 1, name: 'React' }, { id: 2, name: 'Vue' }] },
]
console.log('=== DataDisplay с разными состояниями ===')
states.forEach(state => {
const result = DataDisplay_IfElse(state)
console.log(`[${state.status}] -> type: ${result.type}`)
})
console.log('\n=== StatusBadge ===')
console.log(StatusBadge('active').props.className) // 'badge-green'
console.log(StatusBadge('inactive').props.children) // 'Неактивен'
console.log('\n=== NotificationPanel ===')
console.log(NotificationPanel(5).props.children.length) // 3 (h2 + badge + button)
console.log(NotificationPanel(0).props.children.length) // 1 (только h2)
// Если бы использовали 0 && badge — получили бы "0" в DOM!Создай компонент App, который показывает профиль пользователя с тремя состояниями. Используй useState для хранения булевого isLoggedIn и объекта user. Если isLoggedIn равно false — показывай кнопку "Войти". Если isLoggedIn равно true — показывай имя и email пользователя и кнопку "Выйти". При клике на "Войти" переключай isLoggedIn в true, при "Выйти" — в false.
useState(false) — начальное значение не авторизован. Тернарный оператор: {isLoggedIn ? <блок авторизован> : <блок не авторизован>}. "Войти": setIsLoggedIn(true). "Выйти": setIsLoggedIn(false). Или используй !isLoggedIn для переключения.