awesome-everything EN
↑ Обратно к восхождению

Архитектура фронтенда

Форма состояния: чтение кода

Суть Читай реальные сниппеты React/TS, предсказывай баг формы и выбирай фикс с наибольшим рычагом — derived state, normalized vs вложенно и colocation.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги формы прячутся в коде, который компилируется и рендерится нормально — пока не сработает путь обновления, который он забыл покрыть. Прочитай каждый сниппет, найди второй источник истины или неправильно размещённый state, затем выбери фикс, который сеньор делает первым.

Цель

Отработай цикл, который ты прогоняешь, ревьюя код state: прочитай компонент, заметь значение, которое надо выводить, или данные с неправильной для их паттерна доступа формой, и тянись к структурному фиксу до любой библиотеки.

Сниппет 1 — хранимое выводимое значение

function Cart({ items }: { items: Item[] }) {
  const [total, setTotal] = useState(0);

  useEffect(() => {
    setTotal(items.reduce((s, i) => s + i.price * i.qty, 0));
  }, [items]);

  return <Badge>{total}</Badge>;
}
Викторина

В чём здесь баг формы и фикс уровня сеньора?

Сниппет 2 — вложенно vs normalized

// state.users[].posts[] — user встроен и в поле author каждого post
function renameUser(state, id: string, name: string) {
  return {
    ...state,
    users: state.users.map(u => u.id === id ? { ...u, name } : u),
    // posts по-прежнему несут post.author = {...старый user...}
  };
}
Викторина

Переименование обновляет массив users, но список постов всё ещё показывает старое имя. Какое изменение формы — настоящий фикс?

Сниппет 3 — поднятый, слишком широкий state

// store приложения
const useStore = create((set) => ({
  mouseX: 0, mouseY: 0,
  setMouse: (x, y) => set({ mouseX: x, mouseY: y }),
}));

function Dashboard() {
  const { mouseX, mouseY } = useStore();   // подписка на весь store
  return <ExpensiveCharts /* ререндер на каждом mousemove */ />;
}
Викторина

Почему движение мыши роняет фреймрейт этого дашборда и какой фикс?

Сниппет 4 — серверные данные как клиентский state

function Orders() {
  const [orders, setOrders] = useState<Order[]>([]);
  useEffect(() => { fetch('/api/orders').then(r => r.json()).then(setOrders); }, []);
  // также рендерится в соседнем <OrdersSummary/> со своим идентичным fetch
}
Викторина

Два компонента прогоняют один и тот же паттерн fetch-в-useState. Какой набор проблем создаёт форма и какой фикс?

Итог

Каждый баг формы выше читается прямо из кода: эффект, синхронизирующий значение, которое можно вывести (вычисляй в рендере), авторы, встроенные в посты, так что переименование устаревает (нормализуй в byId + join), высокочастотный state, поднятый в глобальный store, ререндерящий весь мир (колоцируй или возьми срез селектором), и fetch, затянутый в useState, который должен быть server cache (используй библиотеку кэша с ключом по запросу). Прочитай компонент, найди второй источник истины или state, размещённый не там, где его читатели, и почини форму до того, как тянуться к библиотеке.

Продолжить восхождение ↑Форма состояния: отрефакторь state-суп
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.