Comments 14
Мне кажется или 1 и 2 находятся в противоречии друг к другу. Если пофиксить 1 паттерн, то получается исходник ко второму паттерну, если второй пофиксить как предлагается, то получается 1 паттерн.
1-й пункт:
«Не храни в стейте то, что можно вычислить на лету. Лишние рендеры никому не нужны.»
2-й пункт:
«Если данные должны сохраняться между рендерами (например, состояние формы), обычные переменные не подойдут — только useState
или useRef
.»
Где противоречие?
Первый случай — данные уже есть (
props
), просто комбинируем их.Второй случай — данные динамические, и компонент должен их «помнить».
Получается:
Производные значения → вычисляй прямо в компоненте.
Настоящее состояние →
useState
.
Всё, вроде бы, логично, просто контексты разные))
Производные значения → вычисляй прямо в компоненте.
Просто мне непонятно, как можно вычислить someValue без каких то внешних данных. Либо это константа, то ее вынести вовне, либо это вычисляемое из пропсов, что похоже на первый паттерн. Либо вот в стейте хранить.
А, ну тут всё просто на самом деле. Давай на примерах, чтобы стало понятно, где что применять:
1. Константа (вообще не зависит ни от чего)
jsx
const MAX_COUNT = 10; // ← вот это выносим ВНЕ компонента
function Component() {
return <div>Лимит: {MAX_COUNT}</div>; // всегда 10
}
2. Вычисляемое из пропсов (как в 1-м паттерне)
jsx
function User({firstName, lastName}) {
const fullName = `${firstName} ${lastName}`; // ← вычисляем на лету
return <div>{fullName}</div>;
}
3. Состояние (когда нужно "помнить" между рендерами)
jsx
function Counter() {
const [count, setCount] = useState(0); // ← вот это стейт
return (
<button onClick={() => setCount(c => c + 1)}>
Кликнули {count} раз
</button>
);
}
Где твой someValue
?
Если someValue
:
Ниоткуда не берётся (константа) → вариант 1
Считается из пропсов → вариант 2
Меняется внутри компонента и должен сохраняться → вариант 3
Получается: если значение можно получить прямо сейчас из того, что уже есть (пропсы/другие константы) — не пихай его в стейт. Стейт нужен только для того, что компонент должен "помнить" сам.
Надеюсь подробно описал что да как)
Лучше бы я читал эту статью пораньше, когда только начинал. Но все равно освежил память, раскидал в голове все по полкам, за это спасибо)
Полезная статья, особенно для тех, кто только начинает с React. Многие ошибки кажутся мелочами, но потом именно они превращают код в кашу.
Спасибо за статью, хорошо расписано, и расставляет всё по полочкам.
По поводу 17 добавлю, что проблем с мутабельностью начального состояния можно избежать, если его заморозить:
const initialFormState = Object.freeze({
text: '',
error: '',
touched: false,
})
Там, где нужен новый объект на основе этих свойств, можно использовать spread: `{ ...initialFormState, text: 'test text' }`
С другой стороны этот способ несколько противоречит функциональной парадигме Реакта, и также не лишён изъянов. Так что вопрос больше вкусовщины.
Спасибо за хорошее дополнение)
А что, const не достаточно?
const
защищает только переменную от переназначения, но не делает объект неизменяемым: его свойства всё ещё можно изменить.Object.freeze
предотвращает изменение самого объекта и его свойств, что важнее для иммутабельности состояния в React. Поэтому просто const
— недостаточно, если нужна настоящая неизменяемость объекта)
Спасибо за статью, хоть и работаю с реактом уже давно, но всегда полезно проверить сои навыки и освежить память ))
20 частых антипаттернов в React и как их исправить: кратко, понятно, без мифов