Article languages: ????????
When it comes to choosing a state management library for your React application, the options seem endless. A few years ago, Redux was practically synonymous with React, but today, the landscape has expanded significantly. There are Recoil, Zustand, XState, MobX, Redux Toolkit, and many more, with new ones popping up regularly. And all of them are great. The question then arises: Which one should you use? Most likely - none at all. Let's delve into why.
The majority of modern web applications synchronize their state with the backend seamlessly. Updates happen in real-time, and there's no need to hit a "Save" button. For instance, this very article I'm composing in a web editor is automatically saved after a few seconds of inactivity. What remains are small fragments of purely UI-related state, such as determining whether a drawer is open or if text should be expanded. And you don't need a whole state management library to deal with these UI states. React provides a robust set of tools for managing local state.
If you do encounter local state that is not synchronized with the backend and exhibits a complex structure, that's when you should consider utilizing a local state management library like Zustand or Recoil (or any other you like, all of them are actually great). However, this scenario is not applicable to the majority of modern web applications.
Now, let's explore the state management tools available in React and when to use them.
- useState. This is the most fundamental hook for preserving local state between renders. It's an excellent choice for handling primitive data types. Updating arrays and nested objects may be a bit harder.
- useReducer. An advanced counterpart to useState, useReducer requires you to provide a custom reducer function that understands how to create new state based on the old state + some additional data you'll provide. It shines when dealing with complex data structures. You can explicitly define which actions can be performed with your state (like "add new item at the beginning of an array" or "rollback to the initial state") and limit state changes only to these predefined actions. Additionally, useReducer provides a dispatch function which helps to avoid props drilling. IMO this is one of the most underrated React hooks.
- useRef. Although often referred to as a "hook to get imperative access to an element," useRef in fact is a mutable container for any type of data. The key difference compared to useState or useReducer is that mutating a ref container value does not trigger a component rerender (which means that what you see on the screen is potentially not the latest value and you need to handle such cases manually). It is typically employed as a performance optimization, useful when there are frequent state updates, and you wish to avoid rerendering on each update (for example with the onScroll event).
- React.Context + useContext. This powerful combination allows you to share state across all components within a subtree. It's effective at eliminating the need for prop drilling. You can put anything in a context: callback function, [value, setValue] pair from useState, JSX element (why not?). Combining context with useReducer can works as a simplified version of Redux, although there are some significant performance differences to consider. It may not perform optimally when dealing with frequently updated state, as it can trigger a full subtree rerender. Typical use-cases: store current user, permissions, settings, configuration etc.
- React Query (TanStack Query), SWR, RTK Query. For managing "remote" state (synced with the backend) we have these wonderful libraries like React Query. Don't even think about fetching your data in useEffect and storing it with useState, there are too many cases you'll forget to handle.
- URL + routing. The last but not least. All "significant" app state (by "significant" I mean "the state that affects the displayed data") should be stored in the URL. All components have access to it. You can easily share URL with almost no extra effort. The only drawback is that URL has a max characters limit and you won't be able to store a lot of data there (in this case you can think of some URL shortener service or similar solutions).
In conclusion, the key to effective state management in React is choosing the right tool for the job, keeping in mind the nature of the state you're handling, and avoiding unnecessary complexity when it's not warranted.
Коли мова йде про виб?р б?бл?отеки для управл?ння станом React застосунку, вар?анти здаються безмежними. К?лька рок?в тому Redux практично був синон?мом React ? його використовували в кожному першому проект?. Тепер з'явились Recoil, Zustand, XState, MobX, Redux Toolkit ? мабуть ще з десяток б?бл?отек про як? я ? не чув. ? вс? вони добре виконують сво? завдання. Виника? питання: що саме використати в наступному проект?? Скор?ше за все, вам не знадобиться жодна з цих б?бл?отек. Давайте розберемося, чому.
Б?льш?сть сучасних веб-застосунк?в безперервно синхрон?зують св?й стан ?з бекендом. Оновлення в?дбуваються в реальному час?, ? нема? потреби натискати кнопку "Save". Наприклад, цей самий текст, який я створюю у веб-редактор?, автоматично збер?га?ться п?сля дек?лькох секунд безд?яльност?. Залишаються невелик? фрагменти стану, як? пов'язан? лише з UI. Наприклад, визначення того, чи в?дкритий drawer, чи розгорнутий текст. ? для роботи з цими станами вам не потр?бна окрема б?бл?отека з купою крутих функц?й. React ? без сторонн?х б?бл?отек ма? дуже потужний наб?р ?нструмент?в для менеджменту локального стану.
Якщо ви все ж з?ткнетесь ?з локальним станом, який не синхрон?зу?ться ?з бекендом ? ма? складну структуру, тод? варто розглянути використання б?бл?отеки типу Zustand або Recoil (або будь-яко? ?ншо?, вс? вони насправд? чудов?). Проте це довол? р?дк?сний сценар?й для сучасних веб-застосунк?в.
Давайте розглянемо ?нструменти управл?ння станом, доступн? в React, ? коли ?х варто використовувати.
- useState. Це найб?льш базовий хук для збереження локального стану м?ж рендерами. В?н чудово п?дходить для роботи ?з прим?тивними типами даних. Оновлення масив?в ? вкладених об'?кт?в можуть бути складн?шими.
- useReducer. Просунутий аналог useState. useReducer вимага? надати функц?ю reducer, що розум??, як створювати новий стан на основ? старого стану. В?н ся? при робот? ?з складними структурами даних. Ви можете явно визначити, як? д?? можна виконувати ?з вашим станом (наприклад, "додати новий елемент до початку масиву" або "повернутися до початкового стану"). ? вс? зм?ни локального стану будуть обмежен? т?льки операц?ями, як? ви продума?те заздалег?дь. Кр?м того, useReducer нада? функц?ю dispatch, яка допомага? уникнути проблеми props drilling. На мою думку, це один з самих недооц?нених React хук?в.
- useRef. Незважаючи на те, що його часто називають "хуком для отримання ?мперативного доступу до елемента", useRef фактично ? контейнером для будь-якого типу даних. Основна в?дм?нн?сть пор?вняно з useState/useReducer поляга? в тому, що зм?на значення в ref контейнер? не спричиню? ререндер компоненту (це означа?, що на екран? ви можете бачити потенц?йно застар?ле значення, ? вам потр?бно вручну обробляти так? ситуац??). Зазвичай його використовують як оптим?зац?ю, коли присутн? част? оновлення стану ? ви хочете уникнути ререндерингу при кожному оновленн? (наприклад, ?з под??ю onScroll).
- React.Context + useContext. Ця потужна комб?нац?я дозволя? вам поширювати стан м?ж вс?ма компонентами всередин? п?ддерева. Вона ефективно усува? необх?дн?сть у передач? пропс?в на дек?лька р?вн?в дерева компонент?в. Ви можете пом?стити будь-що в контекст: колбек функц?ю, пару [value, setValue] з useState, JSX елемент (чому б ? н??). Комб?нування контексту з useReducer може служити спрощеною верс??ю Redux, хоча це не одне ? те саме. Контекст може працювати не оптимально при робот? ?з часто оновлюваним станом, оск?льки це спричиню? ререндеринг всього п?ддерева. Типов? вар?анти використання: збер?гання поточного користувача, налаштувань, конф?гурац?? ? т. д.
- React Query (TanStack Query), SWR, RTK Query. Для управл?ння "в?ддаленим" станом (синхрон?зованим ?з бекендом) ми ма?мо чудов? б?бл?отеки типу React Query. Нав?ть не думайте витягувати дан? у useEffect та збер?гати ?х в useState, оск?льки ? дуже багато нюанс?в, як? ви забудете врахувати (або код для трив?ально? операц?? виросте до 100 рядк?в коду).
- URL + маршрутизац?я. Останн?, але не менш важливе. Весь "значущий" стан додатку (п?д "значущим" маю на уваз? "стан, що вплива? на в?дображен? дан?") сл?д збер?гати у URL. У вс?х компонент?в ? до нього доступ. Ви можете легко д?литися URL майже без жодних додаткових зусиль. ?диний недол?к - URL ма? обмеження на максимальну к?льк?сть символ?в, ? ви не зможете збер?гати там багато даних (в такому випадку можна подумати про якийсь серв?с скорочення URL або под?бн? р?шення).
На завершення сл?д сказати: 1) думайте, який ?нструмент найб?льше п?дходить для вир?шення конкретно? проблеми; 2) уникайте зайво? складност? якщо це можливо.
Якщо вам сподобалась стаття ? ви хочете д?знатись б?льше про тонкощ? розробки на React, зав?тайте на м?й Udemy курс укра?нською мовою:
Senior Software Engineer
11 个月Awsome stuff?