Галоўная / Артыкулы

Аналіз React Hooks за дапамою атрыбутаў render snapshots і компромісаў.

Высокагарадусны модель мышлення для React і React Native Hooks: атрыбуты render snapshots, effects, refs, мемуізацыя, спецыяльныя Hooks, а таксама як поясніць компромісы пад спытаннях.

Выдавец: REACTAPP.TOP26 хв чытання6055 слоў

Крыніцы: React & React Native Hooks: The Complete Senior Interview Guide

У адміністратыўных спэктараў на React і React Native рэдка канчыцца толькі пытаннем «Што робіць гук?». Пытанне пра useState за хвіліну ператвараецца на разговор пра групаванне і функцыональныя апдэйты; пытанне пра useEffect стае тэмай для дыскусій пра бесканечныя цыклы, застарэлыя клаудзы, анулювання і ситуаціі, калі эфект узагалі не павінен існаваць; useMemo і useCallback прыводзяць да пытання «Чы гэта даслівна прыяўляе швальнасць?». Запамятаваныя вазначэння не вытрымаюць такіх дальнейшых запытанняў. Тое, што застаецца, — это сувязаная модэль, і гід стварае самэў такую: атрыбуты відраслі, пасля чаго — стан, клаудзы, эфекты, рэфы, мемуізацыя, спецыяльныя гукі, швальнасць, а ў канцы — архітэктура, з прыкладамі на React Native там, дзе платформа зменяе ситуацыю.

Модэль атрыбутаў відраслі, ад якой залежыць усё іншае

Пачніце з адной ідеі і трывайце ў яй: калі React атрыбутуе компонент, ён зноў вызывае функцыю компонента з самага пачатку. Выберіце як можно меньшы лічылак.

function Counter() {
  const [count, setCount] = useState(0);
return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}

Здаецца прыродным думаць, што клік на кантактны элемент прыносіць змяны ў сам компонент і прабывае зменіць зменную count. Але так не адбываецца. Функцыя-зменнік просіць React атрыбуэць компонент занова, і наступны вызыв Counter прыме новую значэнне як аблікованую сталячую вялічыну.

Render 1
count = 0
     ↓
User clicks
     ↓
setCount(1)
     ↓
React schedules another render
     ↓
Render 2
count = 1

Кожны раз падзець мае свой апвіт своих пропсаў і стану. Нічога ў тым апвіце не зменшваецца пасля таго. Гэты просты факт адказвае на пытанні пра закрыцця над старымі значэннямі, застарелы стан у таймерах, прычыны наявнасці залежнасцэў эфектаў, адносна таго, як паводзяцца калбэкі і адпаведнікі змаганняў, а таксама прычыны таго, чаму мемазія стосуецца ідэнтычнасці. Адпраўальнікі на спытаннях, якія выходзяць за межы API, зазвычай прабуюць з’ясаваць, чы ў вас ёсць такая модэль.

useState: стан, які належыць падзецу

useState дае функцыйнаму компоненту частку стану, якая задае його выходны данны.

const [count, setCount] = useState(0);

Пара, якая вам вяртаецца, мае два разныя ролі: значэнне для текущага падзецу і функцыя, якая запрашвае апдэйт.

 count
   ↓
current state


 setCount
   ↓
request a state update

Выклік сетара не мутыруе нічога на месцы; ён плануе іншы падзец, у яком компонент бачыць новае значэнне.

Як падымаюцца запытанні

Тыповы ўзрост парадоксу выглядае так:

  • Базавы: якую проблему рашае useState?
  • Середні: чым стан разлічваецца ад звычайнай локальной зменны?
  • Професійны: калі правым выборам ёст мэтада апдейта?
  • Глыбокі: што на самай працэ ўтварае setCount(count + 1), павтораны тры разы ў аднам хэндлеры?

Фраза «Ён зберагае стан» адпавядае толькі першай пункцыі. Абодвальнейшая адпаведзь включае ў сябе модэль:

useState зберагае значэнне, якое вплывае на тое, што атрыбутвае компонент. Выклік мэтады апдейта стварае чергу на новую атрыбутвацыю з апдэйтаваным значэннем, таму стан лепша спрыяць як частку кадру гэтай атрыбутвацыі, а не як зменную, яку можна мяніць. Калі наступнае значэнне залежыць ад пярэднего, мэтада апдейта ёст безпечным выборам, таму што React групавае апдэйты, а зафіксаванае значэнне можа вже быць застарелым.

У працоўным разліку, звычная зменна перзначаецца своім пачатковым значэнням ў кожны раз, калі відбываецца рендерынг, і змена яе ніколі не вымагае ад React практыкування рендерынгу знову, таму яна не можа кераваць інтерфейсам.

Чаму важлівы функцыйнальныя апдейты

Ёсць тры способы адбівання значэння, якія выкарыстоўваюць зафіксаванае значэння.

setCount(count + 1);
setCount(count + 1);
setCount(count + 1);

Усі тры способы чытаюць адно і то ж count з ныявечнага скэнару, таму пасля агрэгавання результатам являецца адна збільшэння, а не тры. Форма апдейтара выправляе гэта:

setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);

React перадае кожнаму апдейтару найсвежэйшае значэння, якое ўсё ще чакае на обробку, таму тры вызовы працююць правільна. Выбірайце эту форму, калі калькuluецца колька апдейтав разам або ў черге, калі калебэк можа запрацаваць з старым скэнарам, і загалом калі новае значэння выраховваецца з старага.

Корзіна пакупак у аплікацыі React Native — это рэалістычны прыклад. Колькасць прадставляецца на экране, таму яна апавяжана з статусам:

const [quantity, setQuantity] = useState(1);

Функцыі-обрабоўчыкі вылучаюць наступную значэнне з пярэдней, а функцыя змены колькасці обмежвае яе на адзину, тады корзіна ніколі не пакажае нуль чы сяродзе моўнае колькасць.

const increaseQuantity = () => {
  setQuantity(prev => prev + 1);
};const decreaseQuantity = () => {
  setQuantity(prev => Math.max(1, prev - 1));
};

Нічога не змяніцца межу вебам і натыўным праграмаваннем

Хук ўзімае аднойчыны на обох платформах. У веб-версіі выпадаючы список можа фіксаваць, чы ён адкрыты:

const [isOpen, setIsOpen] = useState(false);

Экран у React Native таксама можа фіксаваць видныцю модальнага вікна:

const [isModalVisible, setModalVisible] = useState(false);

Механізм статусу ўзаецца; разлічаюцься толькі элементы UI, якія ён керуе.

useEffect: сінхронізацыя з зовнішнім светам

Няхайшы элемент не стварае большэйшую колькасць пытак у размове пад час адзінавання. Звычныя апісанні на кшталт „гэты элемент выконвае код пасля адзінавання“ ўсё ж правдывыя, але не выявляюць сэрца проблемы. Кращы спосаб апісання – эфект дапамагае падтрымваць сінхроннасць вашага компонента з системай, якую React не кантролюець.

У вебе такойя системай можа быць DOM-здарэння, API прыгледача, WebSockets, падпісанні, таймеры або запиты да сеті. У React Native частаюць AppState, модуль Keyboard, натыўныя модулі, прыемнікі паведамленняў, стан сеті, API прыгледача, WebSockets і таймеры.

Як развіваюцца пытанні

  • Базавыя: для чаго викорыстоўваецца useEffect?
  • Середнія: паспрабуйце поясніць, як працюе масіва залежнасцяў.
  • Для аднаго з кандыдацоў высокага рангу: што можа змусіць эфект ціклізавацца без канца?
  • Для аднаго з кандыдацоў высокага рангу+: у якіх ситуацыях эфект ёсць непадходячым інструментам?
  • У рэальных умовах: як заканчыць падпісанне на WebSockets?

Паруйце „useEffect выкорыстоўваецца для вызоваў API“ з гэтым:

useEffect існуе галоўна для таго, каб компонент заставацца сінхронным з зовнішняй системай. Маса залежнасцяў паказвае рэакtyвныя значэнні, якія чытае эфект, і яна павінна быць точной; правіла лінтингу Hooks выяўляюць прычыны ўпускаеў. Калі эфект стварае падпіску, запускае таймер або прыўязвае слухача, ён вяртае функцыю для чысткі, каб стары ресурс быў зваліваны перад тым, як эфект запрацюе зноў, або калі компонент будзе зняты.

Чытанне масы залежнасцяў

За наявнасці залежнасці эфект запрацоўвае пасля першага атрыбутавання і зноў кожны раз, калі гэтае значэнне зміняецца межы атрыбутаваннямаў.

useEffect(() => {
  fetchUser(userId);
}, [userId]);

Калі масы залежнасцяў нема, ён запрацоўвае пасля кожнага атрыбутавання:

useEffect(() => {
  // ...
});

Калі маса залежнасцяў пустая, ён запрацоўвае пасля таго, як компонент будзе атрыбутаваны:

useEffect(() => {
  // ...
}, []);

Будзьце абярожны з фразай «раніцца адна раз». У режыме развіцця Strict Mode спецыяльна запускае, чыстае і знову запускае эфекты, каб выявіць тыя, якія не чыстаюцца належным чынам. Точная формулівацыя такая: эфект з порожнім масавам запускаецца пасля запуску, а режым Strict Mode у режыме развіцця можа дадаць дапаможны цыкл налаштавання і чысткі. Код, який не працуе пасля такога двойнага запуску, зазвычай мае справжнія баги.

Чыстка запобегае вытэканню ресурсоў

Падпіс, які залежыць ад userId, стварае з’ѐеднанне ў тэле эфекту:

useEffect(() => {
  const subscription = subscribeToUpdates(userId);

і вяртае функцыю, якая яго закрывае:

  return () => {
    subscription.unsubscribe();
  };
}, [userId]);

Калі userId зменяецца, React запускае паказаную раней чыстку перш чым запусціць новы эфект, таму ніколі не існуе больш адной актыўнай падпісацыі:

Old effect
    ↓
cleanup old subscription
    ↓
new effect
    ↓
subscribe using new userId

Пад час зняцья эфекта запускаецца пасляпэўная чыстка, і нічога не застаецца:

component unmounts
        ↓
cleanup
        ↓
subscription removed

Той жа механізм выкорыстоўваецца для абрырання асінхронных задань. Для запыту fetch можна абараніць выконанне запытку за дапамою AbortController у функцыі cleanup, або задаць флаг ignore, каб пазнейшы адпаведзень з старог userId не перазапісваў новейшыя данні.

Прыклад у React Native: реакцыя на AppState

Болей пераканлівы эфект, чым запыт да дадзеных, — гэта той, які прыяўязуе компонент да жыцёвага циклу натыўнай аплікацыі. Шэрыфер падтварае данні, калі аплікацыя вяртаецца на першы план, і знікае пасля таго, як компонент адключаецца.

useEffect(() => {
  const subscription = AppState.addEventListener(
    "change",
    nextState => {
      if (nextState === "active") {
        refreshData();
      }
    }
  );
return () => {
    subscription.remove();
  };
}, []);

Это сінхронізацыя з зовнішняй системай у ўсё бол ясной форме, і гэта пакажае спрацоўніку, што вы розумееце, для чаго патрэбны эфекты.

Памылка: сінхронізацыя похадных значэнняў чераз эфект

Падазроўце, што форма зберагае імя і працоўнае апавяданне:

const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");

Часта анти-патэрна — якія дадзе трэці элемент стану і эфект для його апдэйту:

const [fullName, setFullName] = useState("");
useEffect(() => {
  setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);

Это прыводзіць да зайвага стану і дадзе дапэўнейшую перрадзіцю ў кожны раз, калі зменяецца імя, а пад час аднай перрадзіцы цэлае імя ўстарелае. Паколькі значэнне цалкам выначваецца за дапамогою існуючага стану, яго трэба вырахаваць пад час перрадзіцы:

const fullName = `${firstName} ${lastName}`;

Принцып, які варта сказаць вогулом: якщо значэнне можна вырахаваць сінхронна з пропсоў або стану, то яго трэба вырахаваць пад час перрадзіцы, а не зберагаць і падкоряцца ефекту. Гэтая патэрна дакладней рассматрываецца ў stop syncing state with useEffect.

Памылка: нестабільныя залежнасці аб’ектаў

Ёнклудзіцца стварэнне літералу аб’екта ўнутрь компанента і яго уключэнне як залежнасці.

const filters = {
  status: "active",
};

useEffect(() => {
  fetchUsers(filters);
}, [filters]);

Кожны раз, калі відбываецца рендерынг, ствараецца новы об’ект filters, а залежнасці порৱняюцца па ссылкі, таму кожны раз эфект бачыць „змену“. Якщо эфект апдейтуе стан, цыкл продовжваецца сам сабой:

Render
 ↓
new filters object
 ↓
dependency changed
 ↓
effect
 ↓
setState
 ↓
render
 ↓
new filters object
 ↓
effect again

Рэфлексіўны спосаб прынятка — абгорнуць об’ект у мемаізацыю:

useMemo(...)

Перш чым гэта зрабіць, запытайцеся, чы робіцца стварэнне об’екта ў самай компаненте важна. Константа можа быць розмешчаная за межамі компаненты; значэнне, створанае з пропсаў, часта можа быць замененае паляганнем на яе прымітныя поля (на прыклад, status) аб ўтворэннем яе ў самым эфекте. Прынятны спосаб — паспрабаваць выправіць поток дадзеных; дадаўшы мемаізацыю, каб яго схаваць, — гэта не ўсё.

useRef: памяць, якая не рэндаруецца

useRef вяртае змінны контейнер, які застаецца стабільным праз усе рендерынгі, і якога змены ніколі не спрычыняюць новага рендерынгу.

const valueRef = useRef(0);

Вы чытаете і запісвайце ўсё, што ў яму є, через атрыбут current:

valueRef.current

Як толькі прызначаецца новая значэнне, як паказана нижчэй, контейнер апдэюецца, але экран застаецца такім, як і раней, пакуль неякае інша дзія не спрычыніць перрэндаруванне.

valueRef.current = 10;

У спытальніках зазвычай пераходзяць ад пытання «У чым разлік між ref і state?» да пытання «Дайце рэальны прыклад», а потым — да пытання «Чаму тут выбраць ref заместо state?». Хораша адпаведзь:

State — это даны, якія вплываюць на процес рэндарування, таму ўзміненне іх спрычынае перрэндаруванне. Ref заставляе зміннае значэнне застацца актуальным праз усі перрэндарування, не спрычынюючы перрэндарування, калі змінюецца .current. Ref падходзяць для імператыўных об’ектаў, ID таймераў, пакойшых значэнняў і іншых змінных дадзеных, якія не адображаюцца на інтерфейсе.

У вебе ref дае можлівасць імператыўнага доступу да вузла DOM, напрыклад, для фокусавання пола введэння:

const inputRef = useRef<HTMLInputElement>(null);

inputRef.current?.focus();

У React Native ref-ы таксама ўжываюцца часта, адколі мобільныя інтерфейсы часта патрабуюць імператыўных вызовоў, такіх як прасуванне спіса назад да верху:

const scrollRef = useRef<ScrollView>(null);

scrollRef.current?.scrollTo({
  y: 0,
  animated: true,
});

Выбір между станам і ref-ам

Рашаючы пытанне простае: чыяжэ зменіць гэтую значэнне, каб змянілася тое, што бачыць корыстнік? Якщо так, тады выберыце

useState

а якщо ні, тады выберыце

useRef

Актыўныя вікні явна вплываюць на інтерфейс, таму гэта ёсць стан:

const [selectedTab, setSelectedTab] = useState("home");

Об’ект таймера існуе толькі для таго, каб пазней моглы яго абанаваць; нічто його не відображае, таму ref-ы є правым выборам:

const timerRef = useRef<number | null>(null);

useMemo: кэшаванне вырахаванага значэння

useMemo зберагае рэзультат вырахунку і перысчытае яго толькі тады, калі змянюецца якая-небудзь залежнасць. Класычным прыкладам є фільтраваны і сортаваны спіс корыстнікаў:

const filteredUsers = useMemo(() => {
  return users
    .filter(user => user.active)
    .sort(sortUsers);
}, [users]);

Калі users застаўляе той самы рэферэнс, фільтрацыя і сортаванне прахоўваюцца пад час наступных атрыбутаў. Зважайце на скрытыя вымаганні: sortUsers таксама павінен быць стабільным, або ж яго павінна быць указана як залежнасц; кроме таго, функцыя sort мутуюць на месцы, што ў даным случае є безпечным толькі таму, што filter вяліка частка ўжо створыў новы масэвак.

Часта задаецца пытанне: «Чы не трэба яго вжываць у всіх месцах?». Адпаведзь, якая паказвае критычнае мненне:

Ня. useMemo не є безкоштовным: янь дадае трэйкінг залежнасцяў, адзначае выкарыстоўванне памяці і коду. Ён стае корыстным, калі дорогі вычысленні выканаюцца занова без прычыны, або калі стабільны рэферэнс мае значэнне для наступных частак коду; перш чым яго дадаць, трэба адмацірацыю.

Надмерны выкарыстоўкой є мемаізаванне спаўнення стрэлкі:

const fullName = useMemo(
  () => `${firstName} ${lastName}`,
  [firstName, lastName]
);

Робота ў такім случыку простая, таму звычны выраз як быстрей чытаецца, так і прыблізна на той жа скорасці выконваецца:

const fullName = `${firstName} ${lastName}`;

Тепер розглянем прыклад дапрыязду для торгавлі ў React Native, які паказвае тыясячы транзакцый, фільтруюцыхся па символу і прыметку часу, а таксама сортаваных па прыоритэце «найсвежэйшыя першыя».

const visibleTrades = useMemo(() => {
  return trades
    .filter(trade => trade.symbol === selectedSymbol)
    .filter(trade => trade.timestamp >= startTime)
    .sort((a, b) => b.timestamp - a.timestamp);
}, [trades, selectedSymbol, startTime]);

Калі такая трансформацыя становіцца значна дорогай, тады мемаўванне стае неабходным. Нават тады трэба паказаць гэта за дапамою інструменту аналізу, а не проста выкладваць гаданні.

useCallback: зберагчы ідэнтычнасць функцыі

useCallback вяртае той самы об’ект функцыі между кожным перзапускам, пакуль не зменяюцца яе залежнасці.

const handlePress = useCallback(() => {
  submitOrder();
}, [submitOrder]);

Разлік з useMemo заключаецца толькі ў тым, што кэшуецца. Одна з іх кэшуе цяжыню:

useMemo
→ memoises a value

А іншая — кэшуе ссылку на функцыю:

useCallback
→ memoises a function reference

Фактычна useCallback(fn, deps) паводзіцца як useMemo(() => fn, deps). Адказ «Каб падбаць пра скорасць» ёсць слабы на запитанне «Чаму яго вжываць?». Кращы адказ:

useCallback зберагае стабільнасць ідэнтычнасці функцыі, калі ўсе яе залежнасці застаюцца тымі ж. Яго варта вжываць там, дзе справжня роўнасць канкэрэнтаў мае значэнне, напрыклад, калі функцыя-кантракт перадаецца мемаванаму дзецявому компоненту або калі іншы Hook выказвае яе як залежнасць. Яго не трэба дадаваць за замовчаннем, таму што мемавацыя сама па сабе мае своія затраты і ускладнюе розумэнне коду.

Як яно з’яўляецца у звязку з React.memo

React.memo прыменшае перерасчыткі компонента, калі всі яго пропсы ў поверхневым сэнсе роўныя паканунашням.

const UserRow = React.memo(function UserRow({
  user,
  onPress,
}) {
  return (
    // ...
  );
});

Якщо родны компонент кожны раз перадае новую функцыю-стрэлку, адна з пропсаў завжды будзе разным:

<UserRow
  user={user}
  onPress={() => openUser(user.id)}
/>

Паколькі зменяецца рэферэнс, поверхневая парадоражнае не выконваецца, і рэндаруецца запам’ятаваныя строка, што можа таямніча знехтуваць перавагамі React.memo. Стабільны хэндлер выглядае так:

const handlePress = useCallback(() => {
  openUser(user.id);
}, [user.id]);

і ён перадаецца заместо функцыі-інлайна:

<UserRow
  user={user}
  onPress={handlePress}
/>

Памятаце пра адзін важны момант: робіце гэта тады, калі ёсць доказы таго, што рэндараванне дзецячай компаненты ведае большых зусиль, а не як інстынктыўнае рашэнне.

Закрыцція і проблема застарэлых значэнняў

Закрыцція — это функцыя JavaScript, а не Hook, але без яных важка разумець хукі. Закрыцце — это функцыя, якая зберагае доступ да зменных тэрыторіі, дзе яна была створана.

function createLogger(name: string) {
  return () => {
    console.log(name);
  };
}

Внутрэняя функцыя все ўсё бачыць name дзеўяносто пасля таго, як createLogger вярнуўся.

Як у React з’являецца застарэлае закрыцце

Разглядзім компанент з лячыкам:

function Counter() {
  const [count, setCount] = useState(0);

і два кантакты: адны, які збяльшвае лічылку, і адны, які після таймауту у адзину секунду фіксае ўмовы лічылкі:

  const handleClick = () => {
    setTimeout(() => {
      console.log(count);
    }, 1000);
  };  return (
    <>
      <button onClick={handleClick}>
        Log Count
      </button>      <button onClick={() => setCount(c => c + 1)}>
        Increment
      </button>
    </>
  );
}

Падазроўваючы, што працоўнік для фіксацыі даных быў створаны пад час атрыбутавання:

count = 0

Якщо вы нажмёте «Фіксацыя лічылкі», а потым колькаразова збяльшыце ёё за тую ж секунду, таймаут усё раве будзе выведаць даны:

0

нават якія пасляэтапныя атрыбутавання вядуць да іншых дзеянняў:

count = 1
count = 2
count = 3

Калебарас таймауту працюе з мэтаданым count з таго атрыбутавання, якое яго створыла, і гэты моментальны стан ніколі не зменшыцца.

Паспяшнае адказаў у інтэрв’ю

Компаненты на асабліванні працуюць у фрагментах атрыбутаў рэндару: кожны рэндар мае своія значэння і свае замкнутыя функцыі. Калебэк, створаны ў аднам рэндары, зафіксавае значэння таго рэндару, таму асінхронны элементы, такія як таймер, можа спазіраваць стары стан. Рашэння залежыць ад патрабавака: функцыйнае апдэйт, правільныя залежнасці эфектаў або викорыстанне ref, калі даскледзяўна патрэбна найсвежэйшая змінная значэння без спрычынення рэндару.

У гэтым адказе называецца прычына і пропонуецца трое рашэнняў з умовамі для кожнага, што самэ гэта і шукае высокіяй рангу спакоўнік.

Адзіночныя хукі: пакаванне поведэння

Адзіночныя хукі — гэта месца, дзе спалучаюцься паказваныя раней ідэі. Адзіночны хук — это функцыя, назва якой пачынаецца з use і якая вызывае іншыя хукі, чым можна выдзельваць поведэнне з станам і перысвайць яго. Завантажчык прафіля пачынае з адзначэння свайго стану:

function useUserProfile(userId: string) {
  const [data, setData] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

Потым выконваецца апыт запрашчэння дадзеных за дапамою эфекта, прыязванага да userId, і вяртаюцься тры значэння:

  useEffect(() => {
    // fetch user
  }, [userId]);  return {
    data,
    loading,
    error,
  };
}

Экран іх выкарыстоўвае, не ведаючы, як функціонуе процес загрузкі:

function ProfileScreen({ userId }) {
  const {
    data,
    loading,
    error,
  } = useUserProfile(userId);

і атрыбуе тое, што яму патрэбна, з рэзультата:

  // UI
}

Заўважыце, што кожны компонент, які вызывае спецыяльны Hook, мае свой незалежны стан; Hook дзеліцься логікай, а не дадзенням.

Што на самай справе пераканвальнік пераглядае

Запытанне «Чы можаце вы напісаць спецыяльны Hook?» на самай справе стосуецца таго, чы можаце вы адгукнуцца на можлівасць викорыстання повтарнае можна выкарыстоўваць абстракцыі паведання. Хорашы кандыдаты вяршаюць так:

useAuth()
useUserProfile()
useNetworkStatus()
usePagination()
useDebounce()
useKeyboard()
useAppState()
usePermissions()
useInfiniteScroll()

У React Native прыкладам можа служыць Hook для статусу сеті. Ён падпісваецца на змяны супрацэсу падключэння, храніць флаг у стане і вяртае функцию для анпідпісвання як частку процедуры чысткі.

function useNetworkStatus() {
  const [isConnected, setIsConnected] = useState(true);

useEffect(() => {
    const unsubscribe = subscribeToNetworkChanges(
      state => {
        setIsConnected(state.isConnected);
      }
    );
return unsubscribe;
  }, []);
return {
    isConnected,
  };
}

Экран для адправкі платежа потым прымае рашэнне на адной з можлівасцей, якія дае рэзультат:

function CheckoutScreen() {
  const { isConnected } = useNetworkStatus();

if (!isConnected) {
    return <OfflineMessage />;
  }

return <Checkout />;
}

Экран не мае жадзення пра тое, як адрэсаваныя сетевыя падпіскі, і гэта незнаёмасць являе сабою весь сэнс абстракцыі.

Іншыя інструменты з набора Hook

У спэцыяльных інтэрв’ю часта прабуеюць выйсці за межы пяці стандартных Hook, таму трэба знаваць болей шырокі спектар.

useContext

Ён чытае значэнне, якое было заданае вышэй у іерархіі.

const theme = useContext(ThemeContext);

Ён падходзіць для спяльных, межвыпадковых задач: тэмы, аутентыкаціі, локалізаціі і налашчэнняў. Ён не ёсць автаматычна бібліятэкай для керавання станам; кожны корыстувач перарысваецца, калі змянюецца заданае значэнне, што мае значэнне для частай змены дадзеных.

useReducer

Калі калькі станоў змянююцца адночасова, окремыя функціі-задаўшчыкі рассеююць логіку пераходу:

setLoading(...)
setData(...)
setError(...)

Редюсер цэнтрызуе яе:

const [state, dispatch] = useReducer(reducer, initialState);

Компаненты описваюць, што адбылася, а редюсер вяршыць выбіранне наступнага стану:

dispatch({
  type: "FETCH_SUCCESS",
  payload: data,
});

Моделюванне переходаў як іменаванных дзеянняў дапамагае легчэй прыгадваць і теставаць складныя станы.

useLayoutEffect

Ён мае той самы формат, што і useEffect, але выконваецца сінхронна пасля таго, як React апдэйтуе структуру, і першым, чым браузер атрыбутуе змест, што неабходна тады, калі трэба змерыць лейаут і выконаць належныя корэктыя прычымо да таго, калі корыстнік не пабачыт жадных перашкод. У React Native таксама є ситуацыі, дзе час атрыбутавання лейауту мае значэнне. Це не шырэйшы варіант useEffect; адтуды, што ён блакуе процес атрыбутавання зместу, частае выкарыстоўванне пагоршвае рэакцыю прыстрою на дзеяння.

useId

Ён стварае стабільныя, унікальныя ID, галоўная мета якіх — забезпечыць належную взаімадзею, напрыклад, для павязвання етыкетки з полем вводу. Ён особліва корыстны ў вебе; у React Native, дзе няма браузерскага DOM, сфера яго выкарыстоўвання є вужэйшая.

useImperativeHandle

Ён дазваляе компаненту самам выбраць, што адгукаваны рэф будзе адкрываць, замест таго каб вылівалася асновная вузел. Компанент, які можна перыявіць, могаць адкрываць можлівасць фокусавання:

ref.current?.focus();

альбо прасування да верху:

ref.current?.scrollToTop();

Такая кантролюемая імператыўная паверхні ўжоць спрыяючая для спакульных компанентаў React Native.

useSyncExternalStore

Это падтрымваны спосаб чытаць з хранілні, якая знаходзіцца за межамі React, чым дае аднаковыя кадры нават праз адночасны рэндарынг. Гэта працоўна тэхніка, але яя выклікаецца пад час дыскусый пра архітектуру і яя служыць базай для бібліятэкаў статаусу.

useTransition

Ён пазначае дзеянні як неняхучыя. Канцэптуальна, няхучыя дзеянні падтрымліваюць адказнасць інтерфейсу:

Urgent update
     ↓
Keep UI responsive

тады як неняхучыя дзеянні могуць быть запланаваныя з нижэйшай прыорітэтам:

Non-urgent update
     ↓
Can be scheduled with lower priority

Гэта мае значэнне ў большых прыкладах практычнае, дзе дорогі процес перысвечвання інтэрфейса конкуруе з процесамі введення тэксту або нажатыяў.

useDeferredValue

Ён адкладае вычысленне значэння, ўнаследкі чаго спрытныя адносы з корыстнікам застаюцца. Класычны сцэнарый:

User typing
     ↓
Keep input responsive
     ↓
Defer expensive filtering

Введаны тэкст апошнаваецца немедленна, тады калі складны процес фільтрацыі працюе з адкладзеным копіям запиту.

Правіла хуков

Два правілы дапамагаюць хукам працаваць, тым што React ідентыфікуе кожны хук па порядку яго вызову.

Вызывайце хукі на верхнім рэвэлі

Ніколы не пакладайце хук у ўмову, цикл або ранніяе вярненне:

if (isLoggedIn) {
  useEffect(() => {});
}

Уместа таго пакладзіце ўмову внутрь хука:

useEffect(() => {
  if (!isLoggedIn) {
    return;
  }

і пераказайце значэння, ад каго ён залежыць:

  // logic
}, [isLoggedIn]);

Вызывайце хукі толькі з функцый React

Хукі належаць у функцыйныя компоненты і ў спецыяльныя хукі, а не ў дзейскія функцыі-памочнікі або методы класаў.

Працэвае пра скорасць

„Этан экран медленны, што бы вы зрабілі?“ — гэта часта запытанне. Адказ „дадзіце useMemo і useCallback“ паказвае неправы падход. Кращы адказ:

Першы крок — з’ясаваць, дзе на самай працо витыкае час: як часта атрыбутуюцься компоненты, якія вычысленні ўскладненыя, дзе знаходзяцца межы компонентов, як атрыбутуюцься лісты, як распространяюцца змены стану і якая платформа викорыстоўваецца. Потым трэба аналізаваць і усунуць рэальную прычыну спаду скорасці.

Скорасць React Native — гэта не толькі атрыбутуванне

Вартасць мобільнага экрана распадаецца на калькі слоёў:

React rendering
       ↓
JavaScript execution
       ↓
Native platform work
       ↓
Layout
       ↓
Images
       ↓
Animations
       ↓
Lists
       ↓
Network

Таму, калі экран у React Native працюе з перыявамі, прычыной можа не быць

useState

альбо

useEffect

Гэта можа быць вялікі объём работы JavaScript, занадто частая перрадарыцеўка, вялікія спісы, обробка зображэнняў, завантажэнне анімацый, внутршнія операцыі, затрымкі ў сецыі чы абсалютнае падпісванне статаусу, якое прычыняе актывацыю занадто большага колькасці компонентаў. Знанне таго, дзе шукать сярод усіх гэтых элементаў, і ёст кантраст між высокакваліфікаваным і простым дзеянням у мобільных прыладах.

Вялікі спіс транзакцый

Уявіце дапыт пра фінансы, які перрадаруе тыячы транзакцый. У простай версіи весь процес перасчыляецца занова ў кожны момент перрадарыцеўкі:

const visibleTransactions = transactions
  .filter(...)
  .sort(...)
  .map(...);

Зберагчы результаты фільтрацыі і сортавання ў памяці — гэта адна з разумных мера:

const visibleTransactions = useMemo(() => {
  return transactions
    .filter(...)
    .sort(...);
}, [transactions]);

Гэта рэдкая разва ўсё адпаведна. Таксама неабходныя віртуалізацыя спіса, дешава адрасаванне элементаў, стабільныя ключы, компоненты рэядкаў з мемаізацыяй, строгія падпіска на змест, розумна загрузка адобразаў і адпаведныя взаімадзеянні. У React Native выбор FlatList або FlashList і ўсёякі яго налагоджаны зазвычай мае набліжна большое значэнне, чым адзіны useMemo. Питанне, якое трэба задаць, — гдзе знаходзіцца вузкое месца, а не який Hook дадаць.

Автаналія зместу і архітектура

Context проты бібліятэкі для зберагчыка

На пытанне «Калі вам застосаваць Context заместо Redux або Zustand?» няма ўніверсальнай адпаведзі.

Это залежыць ад стану. Контэкст добра падходзіць для дасягліваа стабільных, спільных для всіх элементаў значэнняя, такіх як тэма чы аднастаўленні. Для складных станоў прыкладнення, якія часта абнавляюцца, патрабуюць селектараў, прыкладнаго пераходу чы дзеяградных падпісаў, зазвычай кращы выбар — спецыяльная бібліятэка станоў.

Важныя паначынкі — это адпаведальнасць за данні і частота ўзнавлення.

Не все патрабуецца ў глобальным хранільніку

Пашыраная архітэктурная памылка — це скупленне всіх значэнняя ў адны хранільнік:

Everything
   ↓
Global store

Уместна з’ясаваць, хто несе адпаведальнасць за кожны элемент стану. Локальны стан компонента павінен карыстацца тымі деталямі UI, якія існуюць кораткае час:

isModalOpen
inputValue
selectedTab

Стан сервера — это данні, якія запрашваюцца з API; іх часта лепей карыстацца спецыяльной бібліятэкай стану сервера, якая керуе кешаваннем і паўторным запрашэнням:

user profile
products
orders
transactions

Глобальны ў стане кліента — это невялікі набор значэнняў, якія практычна дзеліцца многімі экранамі:

theme
authentication state
application preferences

У Інтэрнете часты ў стане павінен знаходзіцца ў URL, каб ён застаўся пасля перзагрузкі і могаў быць выкарыстоўваны як спялунак:

filters
search
pagination

Правіло, якое трэба памятаць: ў стане павінны знаходзіцца тыя даны, якія маюць сэнс у самай вузкай можлівай сферы.

Як гэта застосаваць на экране з інвестыціямі

Уявіце сабе экран для інвестыцый у React Native, які патрэбуе даных рынку, выбранага символа, даных графіка, даных портфеля, керування AppState, можласці прасування і дорогай трансформацыі графіка. Спрасаваная структура:

InvestmentScreen
        │
        ├── useMarketData()
        │
        ├── usePortfolio()
        │
        ├── useAppState()
        │
        ├── useRef()
        │
        ├── useMemo()
        │
        └── useCallback()
                │
                ▼
              UI

Прыгаея скэч компаненту. Є два моменты, якія трэба зазначыць: фрагмент заканчваецца калькамі прозы і разлініюваннямі, якія не ўтвараюць валіднага коду, таму яго трэба чытаць як псевдакод; а таксама setSymbol з useState вже ў стабільным стане, таму викорыстанне обгорткі useCallback тут є неабяжным, і можна безпосередньа перадаць функцію-зменяльнік.

function InvestmentScreen() {
  const [symbol, setSymbol] = useState("AAPL");

const scrollRef = useRef<ScrollView>(null);
  const { data: marketData } = useMarketData(symbol);
  const { portfolio } = usePortfolio();
  const chartData = useMemo(() => {
    return transformMarketData(marketData);
  }, [marketData]);
  const handleSymbolChange = useCallback(
    (nextSymbol: string) => {
      setSymbol(nextSymbol);
    },
    []
  );
  return (
    <ScrollView ref={scrollRef}>
      <MarketHeader
        symbol={symbol}
        onSymbolChange={handleSymbolChange}
      />
      <PriceChart data={chartData} />
      <PortfolioSummary
        portfolio={portfolio}
      />
    </ScrollView>
  );
}

---

Now each concept has a clear responsibility.

---

Кожны Hook тепер мае адну, зрозумелую задачу:

useState
→ selected symboluseRef
→ imperative ScrollView accessuseMemo
→ expensive chart transformationuseCallback
→ stable handler where usefulCustom Hooks
→ market and portfolio behaviouruseEffect
→ internal synchronization within those Hooks

Эфекты застаюцца ўнутрь самых кастомных Hooks, дзе яны сінхронізуюцца з падачай даных з рынку і жыцёвым циклам прыкладнення, а сам экран практычна выглядае як опис його даных.

Адказванне як у старшага інжынера

Зайсці на адны ўзлёт вышэй за пытанне

Частая памылка — адказваць толькі на тое, што было запытана. Для пытання „Шта такое useMemo?“ адказ „Яе выкарыстоўваюць для зберагчэння значэння“ є правільным, але лёгка забываемым. Болей шырокі адказ:

useMemo зберагае вырахаваную значэнне, ключоўваючы яго за залежнасцямі. Ён не ўключаны як стандартны элемент, таму што спрыяе додатковым витратам ресурсоў і паўхутарыці логіки. Ён становіць сэнс, калі дорогі вырахунак павтараецца некалькі разоў без неабходнасці, або калі важліва стабільная рэферэнцыя, ідеальна пасля таго, як аналіз працы програмы выявіў узгорнуты этап.

За калькі рэчэй вы паказалі знання API, усведамленне пра прыдатнасць, можлівасці компромісаў і здатнасць да правильнага выбору.

Структура адпаведзі на пяць крокаў

Надзеяны шаблон для будзь-яга пытання пра Hook:

1. What is it?
2. Why does it exist?
3. Real example
4. Trade-off / pitfall
5. React Native example when relevant

Застосаванне да useRef:

  • Што: стойкое зменнае значэнне, яке не вызывае перзапісу элемента інтэрфейсу.
  • Чаму: для дадзеных, якія павінны застацца незмяненымі пасля перзапісу, але не ўтвараюць частку інтэрфейсу.
  • Прыклад: ID таймера або ідэнтыфікатор компонента.
  • Компроміс: паколькі яго адчыненне не вызывае перзапісу, ён не павінен храніць стан інтэрфейсу.
  • React Native: ref дае імператыўны кантроль над ScrollView.
  • Тэмы, якія павинны быць розглянуты

    Па часткі useState будзьте гатовыя обгаварываць функціі апдэйта і групаванне калькаў, чаму змянэнне звычайнай зменной не паўтарае змяны на экране, што спрычынаў перы-рэндер, дзе павінен знаходзіцца адпаведны элемент стата, і як утрымацца ад викорыстоўвання стата, які на самай працэ ёсьць вырахаваным.

    Па часткі useEffect могуць з’явіцца запытанні пра ролю массива залежнасцяў, застарэлыя клаудзы, паўтарныя запускі, для чаго праграмуецца функцыя чысткі, як анулюваць асінхронную работу, ситуацыі, калі эфект ёсьць неправым інструментам, і двойны запуск у режыме Strict Mode пад час разработкі.

    Па часткі useRef будзьце гатовыя поясніць, чаму запіс у .current не спрычынае рэндера, у якіх ситуацыях ref є кращым выборам чым стат, як refs прыўязуюцца да DOM або натыўных відзораў, і што значыць імператыўны API.

    Для useMemo неабяжна чыстая адразумованасць: чы роўна ён завжды прышвартавае працэс, як ён разлічыцца з useCallback, што значыць рэферэнцыйная аднаковасць, калі правім рашэнням будзе яго адключэнне, і як можна паказаць, што ён дапамогаў.

    Для useCallback трэба знать, чаму ідэнтычнасць функцыі можа мяць значэнне, як ёна работае са React.memo, калі ў яе няма жадных практычных выгод, чы ён можа спамітняць працэс, і што павінна быць у яе лісты залежнасцяў.

    Занятка з адлагоджэння

    У спаканавальных кансультаціях любяць даваць компоненты з адмовамі. Ёсць прыклад з калькама проблем.

    function UserList({ users }) {
      const [search, setSearch] = useState("");
    
    const filteredUsers = useMemo(() => {
        return users.filter(user =>
          user.name.includes(search)
        );
      }, [users]);
      useEffect(() => {
        console.log("Search:", search);
      }, []);
      return (
        <List
          data={filteredUsers}
          renderItem={({ item }) => (
            <UserRow
              user={item}
              onPress={() => selectUser(item.id)}
            />
          )}
        />
      );
    }
    

    Відсутняя залежнасць у useMemo

    Мемо сортавана толькі па пользователях:

    }, [users]);
    

    але фільтр таксама чытае

    search
    

    таму ліст ніколі не апдэйтуецца пад час вводу пользователя. У залежнасцях павінны быць якія-такія з або ўсіх:

    }, [users, search]);
    

    Правіло лінтаверкі негаразд заўважыць гэта адразу.

    Эфект зашчылены на першай значэнні

    У гэтага эфекта ёсць порожній масавы:

    useEffect(() => {
      console.log(search);
    }, []);
    

    таму ён запускаецца толькі пасля маунта і фіксуе первыя данні з search. Якщо мета — фіксавацыя кожных змян, трэба на гэта рэгулювацыя:

    useEffect(() => {
      console.log(search);
    }, [search]);
    

    Інлайн-колбэк, які обрабоўваецца з увагай

    Обрабоўчык рэядка

    onPress={() => selectUser(item.id)}
    

    являе сабою новую функцыю ў кожны раз, калі відбываецца рендэр. Сама по сабе гэта не ёсць баг. Спачатку трэба выявіць, чы

    UserRow
    

    ёсць даскладным і чы справды відбываецца непатрэбны рендэр. Толькі на адной такой падставе можна будзе вжыць

    useCallback
    

    альбо пераструктураваць компаненты, напрыклад, пасылаючы id і стабільны обрабоўчык рэядку. Адзін з аспектаў правильнай адпаведзі — гэта стрыманасць.

    Хорашыя, сильныя і професійныя адпаведзі

    Этапы развіцьба легкія да запамятовання. Хорашы разработчык ведае, што такое useCallback. Кваліфікованы разработчык ведае, калі яго трэба выкарыстоўваць. Досвідчыты разработчык ведае, калі яго не трэба выкарыстоўваць, і можа поясніць наследкі такага выбору. Той самы принцып паслужыць для кожнага Hook. Цэль ніколі не ёст

    Maximum Hooks
    

    а радыяй

    Correct abstraction
    +
    Correct data flow
    +
    Correct performance trade-offs
    

    Одны набор паняў для веба і натыўных прыстроёў

    Вам не трэба аднародных знанняяў пра Hook для React і React Native. У вебе прыклады зазвычай стосуюцца

    DOM
    browser events
    Web APIs
    WebSockets
    browser storage
    

    тады як у обгаворэннях пра мобільныя прыстроі йдзе пра

    AppState
    Keyboard
    native modules
    device APIs
    push notifications
    network state
    native event subscriptions
    

    Пры тым основны набор інструментаў у обох варыянтах React адны і тые ж:

    useState
    useEffect
    useRef
    useMemo
    useCallback
    useContext
    useReducer
    Custom Hooks
    closures
    dependency arrays
    

    Таму спачатку поясніце концэпцыю React, а потым пакажыце, як яе застосаваць на данай платформе. Лічыцца, што так вы застанетесь надзеянымі як у веб-розработцы, так і ў розработцы для мобільных прыстроёў і гібрыдных рашэнняў.

    Карточка швырага апісу

    useState

    Элемент, які выклікае перрэндер пасля абнаўлення. Тыповыя варыянты выкарыстання:

    form values
    selected tabs
    modal visibility
    loading state
    local UI state
    

    useEffect

    Сінхронізацыя з зовнішнімі системамі. Тыповыя варыянты выкарыстання:

    subscriptions
    timers
    WebSockets
    AppState
    native events
    network operations
    

    Утримайцеся ад яго для значэнняў, якія можна вылучыць пад час перрэндера.

    useRef

    Стойкое зменнае значэнне, якое не выклікае перрэндер. Тыповыя варыянты выкарыстання:

    DOM/native refs
    timers
    previous values
    imperative APIs
    mutable non-UI data
    

    useMemo

    Значэнне, якое было вырахавана і збераглена ў памяці. Ўжывайце яго, калі

    calculation is expensive
    or
    referential identity matters
    

    спачатку трэба яго вылучыць.

    useCallback

    Рэферэнс функцыі, якая была збераглена ў памяці. Це карытка, калі

    function identity matters
    React.memo is involved
    another Hook depends on function identity
    

    Не дадзейце яго автаматычна.

    useContext

    Чытае спакульнае контекстовае дадзеныя, напрыклад:

    theme
    locale
    authentication
    configuration
    

    useReducer

    Моделюе складныя пераходы. Карытка, калі у вас є

    many related state variables
    complex transitions
    state machine-like behaviour
    

    Custom Hooks

    Павтарна выкарыстоўваная стацыонарная логіка, напрыклад:

    useAuth()
    useNetworkStatus()
    useAppState()
    usePagination()
    useKeyboard()
    useUserProfile()
    

    Ментальная модель заместо гласарыю

    Замест таго, каб запамятаваць адзін-словяныя значэння для кожнага хука,

    useState = state
    useEffect = side effect
    useRef = reference
    useMemo = memoisation
    useCallback = callback
    

    падумайце пра тое, якія зв’язкі ўсё гэта мае з процесам выкарыстоўвання інтерфейса: стан UI праходзіць через useState і спрычыняе паўтарны выкарыстоўвання компонентаў, значэнні, якія выраховваюцца, — через useMemo, зменныя даны, якія не адналежаць да UI, зберагаюцца ў useRef, а useEffect спаявае выкарыстоўваны інтерфейс з API, запускамі падзеяў і вучонымі системамі; усё гэта абгружаецца за дапамогою спецыяльных хуків.

    React Render
                         │
              ┌──────────┼──────────┐
              │          │          │
           UI State   Derived     Mutable
              │        Values       Data
              │          │          │
          useState    useMemo     useRef
              │
              ▼
           Re-render
              │
              ▼
              UI
              │
              ▼
       External Systems
              │
              ▼
          useEffect
              │
              ▼
     APIs / Events / Native
              │
              ▼
        Custom Hooks
    

    Пад усім гэтым лежыць калькі фундаментальных прынцыпаў:

    Rendering
    +
    Closures
    +
    Referential Equality
    +
    Data Flow
    +
    Performance
    

    Калі гэтыя прынцыпы будуць чыста зрозумелы, адзінкавыя хуки стануць простымі для викорыстоўвання.

    Пытанні, якія трэба ставіць пад час кожнага адзорвання коду

    Хукі — это інструменты для керавання разнымі видамі поведзення ў реакtyўных дапытках, і важным навыкам є розумеў, чаму вы викорыстоўваете кожны з іх. Гэтыя пытанні падходзяць як пад час адзорвання коду, так і пад час спэктараў:

    • Чы трэба, каб гэты стан існаваў, чы можна яго вырахаваць?
  • Чы рэальна гэта адзінакошчанасць са чымось званешням, і чы правілі яе залежнасцяў?
  • Чы можа гэты callback чытаць застарелую закрытую функцію?
  • Чы гэты рэф зберагае імператывныя даны, чы стан UI, які павінен быць у useState?
  • Чы гэта мемаізацыя рашае выявленую проблему?
  • Чы гэты спецыяльны Hook маскіруе корисную складнасць, чы проста дадзе адчыненасць?
  • Чы гэты стан павінен быць локальным чы глобальным?
  • Дзе настоямы бутаўбанак выконавачасці?
  • Узагальненне

    Hooks становяцца простымі, калі перестаёш адносіцца да іх як да окремых API і бачыш у іх стадіі аднаго прайму:

    Rendering
       ↓
    State
       ↓
    Closures
       ↓
    Effects
       ↓
    References
       ↓
    Memoisation
       ↓
    Custom Hooks
       ↓
    Performance
       ↓
    Architecture
    

    Якщо врашчаць гэты праг, перайсці з React для вебу на React Native стае значна менш складным. Калі хтось запытае, чаму вы вжылі useMemo у паводзе конкрэтнага месца, не прыходзіце да стандартнага адзначэння і раскажыце цю історію: проблему, яку вы пазілі, прычыну ёй, чаму самэўпарацыйны функцыя падходзіла, якія компрэсіі вы вазавалі і як вы ацэнілі рынак.

    Перад спыткам пераканайцеся, што можете натуральна гаварыць пра:

    • Фундаменталы: прычыны рэндара, што такое кадр рэндара, чым стан вядзецца ад звычайнай зменной, клозуры, рэферэнцыйнае равенства і ўзравнаванне.
  • Хукі: звычайныя п’ять, а таксама useContext і useReducer, спецыялізаваныя (useLayoutEffect, useId, useImperativeHandle, useSyncExternalStore, useTransition, useDeferredValue) і спосабы стварэння власных хукіў.
  • Эфекты: точныя залежнасці, застарэлыя значэння ў калебаках, чыстка і анулювання, кераванне падпісамі, працэўнае сталяванне і спосабы выявлення момента, калі эфект не патрэбен.
  • Прыдатнасць: як React.memo пораўнюе пропсы, вартасць мемуізацыі, аналіз рэндароў, дужа дугія лісты, складныя вычысленні і занадто шырокія падпісы до хранальнікаў.
  • React Native: жыццёвы цикл аплікацыі через AppState, карбаванне клавіатуры, натыўныя модулі і API прыстрою, слухачы, зарэгістраваныя на натыўной стороне, налагоджэнне ліста, раздзелленне роботы межу JavaScript і натыўным кодам, а таксама спосаб, якаму платформа адрасавае змест.
  • Архітектура: локальны протыў што спяльванага стата, даны кліента протыў даных сервера, дзе ставяцца межы спецыяльных Hook-аў, за шта адпаведае кожны компонент, хто ўладае кожнай значэннем, і проектаванне API для перадаўальных компонентаў.
  • Мета — не запамятаванне адпаведных адказоў на всё, а достатнія знаёмства, каб могчыць разумна распрацаваць незнайомую проблему пад час спытання.