Основи Redux для початківців
Redux - Це інструмент для керування станом програми. Побудований на принципах технології Flux та функціонального програмування. Створений компанією FaceBook, але всупереч поширеній думці може використовуватися не тільки у зв'язці з React, але також з іншими фреймворками/бібліотеками.
Flux – це тип архітектури та набір патернів проектування веб-додатків. Детальніше на Wiki
Redux використовує методологію flux. Вона складається з 4 понять:
- Інтерфейс користувача (View) - у React це компоненти
- Сховище (Store)
- Диспатчер
- Action
У React за умовчанням немає якогось глобального state (стану), який був би доступний у всьому додатку. Ви можете лише зберігати дані в межах одного компонента. Наприклад, у вас є інтернет-магазин і в ньому є кошик з товарами. Якщо працювати тільки зі стейтом компонента Кошик, то ці дані будуть недоступні в інших компонентах. Також, наприклад, у вас є іконка кошика в кутку екрана, яка повинна показувати кількість товару, які користувач додав туди. Так от засобами чисто React, це буде складно реалізувати.
Саме тому є такі бібліотеки як Redux, для зберігання всіх даних програми в одному місці та зручного їх оновлення.
Основні поняття Redux
Як я вже писав вище, основні поняття редаксу actions, dispatcher, store.
Store - Це стан веб-компонента, який зберігає в собі всю інформацію (або ту, яку ви вирішили зберегти в нього). Надалі сторінка буде доступна з будь-якого компонента вашої програми.
Action - Дія, що визначає що потрібно зробити. Відповідно до принципів функціонального програмування, ми не можемо змінювати об'єкт безпосередньо, тому нам потрібні екшени, щоб передати їх у диспатчер і «сказати», що потрібно зробити.
Dispatcher — повідомляє сховищу про якусь дію (action) та передає йому оновлену інформацію.
Тепер коли ми розібрали основні поняття, давайте подивимося, як працює Redux:
Схема роботи Redux
Компонент генерує дію (action), диспатчер повідомляє про це сховище (store), сховище змінює стан та дані передаються в компонент (View).
Є ще одне поняття у Redux – це reducer (редюсер). Редюсер – це чиста функція, яка приймає як аргумент сховище та екшен. Основні правила редюсерів:
- У цих функціях повинно бути «side effects» (побічних ефектів). Наприклад, не можна робити API запит для отримання будь-яких даних
- Вони не повинні мутувати (змінювати) прийняті аргументи чи стан.
- Не можна викликати нечисті функції всередині редюсерів (наприклад, Date.now() або Math.random())
Докладніше про чисті функції можна прочитати тут.
Простий приклад використання Redux
Тепер на простому практичному прикладі розберемо, як працювати з Redux.
Ми зробимо простий додаток ToDo, який дасть можливість створювати свої таски зі збереженням їх у store. Це буде простий додаток для прикладу, основний наголос зроблений на роботу з Redux.
Отже, є два варіанти, ви можете завантажити стартовий проект і просто запустити установку, або покроково пройти і створити проект зі старту.
Встановлення та налаштування проекту
Щоб створити програму заново, відкриваємо командний рядок або Git Bash
npx create-react-app redux-first-app
Далі заходимо в папку проекту та встановлюємо Redux та пакет для Реакту – react-redux
cd redux-first-app/ // Потім npm i redux react-redux
Якщо ви завантажили архів з готовим додатком, тоді його потрібно розпакувати, увійти в папку з додатком і в командному рядку/терміналі запустити команду:
Тепер, щоб запустити наш проект, потрібно скористатися наступною командою:
Проект буде зібраний та запущений, автоматично відкриється вкладка у браузері
Результат виконання команди npm run start
Для того, щоб не верстати все знову, ми використовуємо Bootsrap. Давайте його встановимо.
npm install react-bootstrap bootstrap
Також підключимо стилі у файлі src/index.js
import React from 'react'; import ReactDOM від 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals від './reportWebVitals'; import 'bootstrap/dist/css/bootstrap.min.css'; ReactDOM.render(, document.getElementById('root') ); reportWebVitals();
Створення базової структури для сховища
Тепер зробимо базову структуру для Redux. Створимо папку src/store, а в ній 4 файли
- actions.js
- actionTypes.js
- reducer.js
- store.js
Спочатку визначимо, які типи екшенів нам потрібні у файлі actionTypes.js. Можна типи і не визначати, але надалі це дасть нам змогу скоротити час на дебагінг, якщо раптом знадобиться змінити ім'я екшену, всі вони знаходяться в одному місці, що теж зручно. Якщо нам знадобиться екшен в іншому місці, нам буде достатньо імпортувати його в іншому модулі.
У нашому додатку, наприклад, нам потрібен буде екшен у 2 файлах: actions та reducer. Створимо файл actionTypes.js і в ньому визначимо наші типи:
export const TASK_ADD = 'TASK_ADD'; export const TASK_TOGGLE = 'TASK_TOGGLE'; export const TASK_REMOVE = 'TASK_REMOVE';
У файлі store/actions.js ми опишемо всі екшени, які нам потрібні для застосування:
import * as actions from './actionTypes'; export const addTask = task => (<type: actions.TASK_ADD, payload: task>); export const toggleTask = (<type: actions.TASK_TOGGLE, payload:<id>>); export const removeTask = (< type: actions.TASK_REMOVE, payload: < id >>)
Вище ви бачите типову структуру екшену: це функція, яка повертає об'єкт із двома властивостями:
- type - Тип екшену (ми його визначали в actionTypes)
- payload - дані, які нам потрібно передати до редюсера
Тепер розглянемо функцію редюсер (store/reducer.js):
import * as actions from './actionTypes'; let lastId = 0; export default function reducer(state = [], action) < switch (action.type) < case actions.TASK_ADD: return [. state, < id: ++lastId, title: action.payload.title, completed: false, >]; case actions.TASK_TOGGLE: return state.map(task => < if (task.id === action.payload.id) return < . task, completed: !task.completed >return task; >); case actions.TASK_REMOVE: return state.filter(task => action.payload.id !== task.id); default: return state; > >
Тут ми імпортуємо наші типи екшенів, потім визначаємо змінну для того, щоб задавати ID кожному новому таску.
Сам reducer приймає як аргументи state (або дорівнює порожньому масиву) і екшен. Далі ми перевіряємо тип екшену і залежно від цього робимо певні маніпуляції зі стейтом.
Давайте розмеберемо з прикладу екшену TASK_ADD. При додаванні нового таска, нам необхідно зробити копію поточного стейту та додати до нього новий таск
Далі на підставі цього редюсера нам потрібно створити store за допомогою функції createStore. Створимо файл store.js з таким вмістом:
import < createStore > від 'redux'; import reducer від "./reducer"; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); export default store;
* У мене також другим параметром додано слід. рядок
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
Це для роботи плагіна для Chrome – Redux DevTools. Зручний плагін для дебагінгу.
Тепер після створення store, ми можемо використовувати його у будь-якому модулі нашого додатку.
Основні методи роботи зі store
- store.dispatch() - Диспатч будь-якого екшену
- store.getState() - Отримання даних, які зберігаються в store
- store.subscribe() - Підписка на зміни store.
Redux у функціональних компонентах (хукі)
Наш додаток буде побудовано за допомогою функціональних компонентів та хуків, тому ми трохи розглянемо, які хуки надає нам Redux для роботи в таких компонентах.
- useDispatch() — заміна mapDispatchToProps(). Цей хук повертає метод dispatch. За його допомогою ми потім можемо диспатчити потрібні екшени.
- useSelector() — аналог mapStateToProps() — Цей хук приймає колбек, який як аргумент приймає поточний стан. Ви можете повернути весь state або певні дані з нього.
- useStore() - цей хук повертає посилання на той самий state, який був переданий в . У документації редаксу йдеться про те, що краще цей хук не використовувати часто, а краще useSelector()
Тепер давайте повернемося до нашої програми. В index.js нам потрібно обернути нашу програму в компонент Provider і передати йому store через пропси. Store ми створили у файлі store/store.js
import React from 'react'; import ReactDOM від 'react-dom'; import < Provider >from 'react-redux'; import './index.css'; import App from './App'; import reportWebVitals від './reportWebVitals'; import 'bootstrap/dist/css/bootstrap.min.css'; import store from './store/store'; ReactDOM.render(> , document.getElementById('root') ); reportWebVitals();
Наступним кроком давайте створимо папку, в якій зберігатимемо наші компоненти і назвемо її componetns. У ній створимо 3 файли:
- Task.js — цей компонент відповідатиме за виведення окремого тяга
- TaskList.js - це буде список тяган
- AddNewTask.js — а цей компонент відповідатиме за створення нового тяга
import React від "react"; import < useDispatch >з "react-redux"; import < Form, ListGroup >з "react-bootstrap"; import < toggleTask, removeTask >з "../store/actions"; const Task = (< task >) => < const < id, title, completed >= task; const dispatch = useDispatch(); // Отримуємо диспатч із хука return (> checked= onChange= < () =>dispatch(toggleTask(id)) > // диспатчім потрібний екшен під час кліку по тяганню dispatch(removeTask(id))>>Видалити // Диспатчі екшен removeTask для видалення тяга зі стору) > export default Task;
Спочатку імпортуємо хук useDispatch(), т.к. у цьому компоненті ми будемо диспатчити 2 екшени: виконання таска (toggleTask) та видалення (removeTask). У компоненті у нас є подія onChange - при кліку на цей компонент ми будемо диспатчити екшен для перемикання стану таска, ну а при кліку кнопки видалити - диспатчім екшен для видалення таска з нашого стору.
Далі файл src/components/TaskList.js
import React from 'react'; import < ListGroup >з "react-bootstrap"; import Task from './Task'; // імпортуємо компонент таска, створений раніше const TaskList = (< tasks >) => < if (tasks.length) return (< // пробегаемся методом map по нашему массиву с тасками и выводим для каждый таск tasks.map(task => task= /> ) > ) else return null; > export default TaskList;
Тепер останній компонент AddNewTask.js
import React, < useState > from 'react'; import < Button, FormControl, InputGroup >з "react-bootstrap"; import * as actions from "../store/actions"; import < useDispatch >з "react-redux"; const AddNewTask = () => < const [taskTitle, setTaskTitle] = useState(''); const dispatch = useDispatch(); const handleTaskTitleChange = (e) => < setTaskTitle(e.target.value); >const handleTaskSubmit = () => < dispatch(actions.addTask(< title: taskTitle >)); setTaskTitle(''); > return ( onChange=handleTaskTitleChange(e)> /> >Зберегти
) > export default AddNewTask;
Тут у нас буде 2 обробники handleTaskTitleChange() і handleTaskSubmit() Останній буде диспатчити екшен для створення нового тяга.
Тепер залишився заключний крок, змінити файл scr/App.js та додати трохи стилів
import React from 'react'; import < Container, Colw >from "react-bootstrap"; react-redux"; import './App.css'; function App() < // Отримуємо наш state const tasks = useSelector(state =>state);.main-app-container .list-group-item < display: flex; >.list-group-item .form-check < width: >.list-group-item-actions < width: 10%; -align: right; >.list-group-item-actions span .main-app-container .list-group-item:hover .list-group-item-actions span < display: inline; cursor: pointer; >.list-group-item-actions span:hover < text-decoration: underline; >.list-group-item.task-completed < background -color: #f2f2f2; >.list-group-item.task-completed .form-check-label); > export default App; |
|
> Список завдань
/>
У результаті у нас вийшов такий додаток
Запитання з міткою [redux]
Бібліотека для JavaScript з відкритим вихідним кодом, призначена для керування станом програми. Найчастіше використовується у зв'язці з React або Angular для розробки клієнтської частини.
- Конкурсні 0
- Невідповідні
- Цитовані
- Рейтинг
- Популярні
- За тиждень
- За місяць
- Невідповідні (мої мітки)
Проблема з redux, не бачить store.
Намагаюся зробити щоб модалка завантажувалася окремим чанком і потім довелося через це міняти slice Знайшов таку статтю в документації Redux Toolkit code spliting import < combineReducers, Reducer, .
Чому запит, який ініційований інвалідуванням тега надсилається зі старим access токеном? Rtk query
У мене є форма оновлення даних користувача (нікнейму та аватара) і коли я роблю запит на сервер для оновлення переданих даних, у мене інвалід тег 'User' і робиться новий запит на .
Як оптимізувати перемальовування реактивних компонентів?
У мене є компонент сторінки Events всередині якої лежить компонент EventFilter. При першому завантаженні кожна з них перемальовується 6 разів. Якщо закоментувати EventFilter, то компонент Events .
При перезавантаженні сторінки, у мене обнулюється favorite
При перезавантаженні сторінки, у мене обнулюється favorite. І якщо стояв лайк, то він не стоїть, хоча лічильник і всі дані отримані з data вірні файли з помилкою: import < Link, useNavigate, useParams >.
Питання з архітектури FSD (Feature Slice Design)
Освоюю Feature Slice Design та намагаюся застосувати архітектуру, методологію FSD на своєму особистому проекті. У додатку бувають різні тоггли, перемикання і тд, ось у мене зараз є тоггл по .
Некоректно працює функція через асинхронність React+Redux
Така проблема. Є код, в ньому функція resultToColor().Код з пет-проекту, звичайна гра в колесо червоне-чорне Функція виводить колір, який прийшов із сервера і пішов у redux-state. Зараз вона.
Використовуючи функцію connect з react-redux не передаються пропси компонент
Ось весь код, який відповідає за логінізацію на сайті import < Field, reduxForm >from "redux-form" import < withElement >from "../common/FormsControl/FormsControls" import < .
TS2769: No overload matches this call
Тільки почав вивчати TypeScript і зіткнувся з такою помилкою: Overload 1 of 3, (thunkAction: ThunkAction
Помилка в типізації dispatch
Підкажіть, хто стикався з такою помилкою при роботі з createAsyncThunk? Argument of type 'AsyncThunkAction
Поле введення тексту втрачає фокус після введення символу
Намагаюся зробити простий додаток, який відображатиме на сторінці введений текст. По-перше, введений текст не відображається. По-друге, поле введення втрачає фокус після кожного введеного .
Де зберігати багато картинок для сайту на React? [закритий]
Я зелений новачок. Розробляю pet-проект на React суть якого – велика галерея картинок. Зіткнувся із запитанням, де мені всі ці картинки зберігати. Невже потрібно кожне зображення додавати до .
Як кешувати дані компонента у react сесії?
Зупинився використання redux. Є сторінка на сторінці приблизно 5 фільтрів. Кожен фільтр - це колекція, яка підвантажується з API. Реалізував зберігання отриманих даних для КОЖНОГО фільтра.
Конфлікт React та react-redux
Я закінчив проект, який легко запускається локально на комп'ютері. Але при спробі вивантажити його на Vercel отримав таку помилку: Я пробував по-різному вирішити цю проблему. І за допомогою -legacy-.
Не працює mock-server MSW та RTK хук висить у стані pending
Пишу тест, який перевірятиме роботу хука з Redux RTK Query. Намагався зробити mock сервер використовуючи MSW бібліотеку, але з якоїсь причини сервер не працює, а хук при запиті висить у стані .
Redux проти MobX без плутанини
В останні роки я часто використовував Redux, але останнім часом використовую MobX як альтернативу керуванню станом. Здається, що альтернативи Redux природно перетворюються на безлад. Люди не впевнені, яке рішення вибрати. Проблема не обов'язково "Redux vs MobX". Щоразу, коли існує альтернатива, людям цікаво, як найкраще вирішити їхню проблему. Я пишу ці рядки, щоб усунути плутанину навколо рішень щодо управління станами Redux та MobX.
Про що буде ця стаття? По-перше, я хочу коротко повернутися до проблеми, яку вирішує бібліотека управління станом. Зрештою, все буде добре, якщо ви просто використовуєте this.setState() і this.state в React або його варіацію в іншій бібліотеці рівня вистави або в середовищі SPA. По-друге, я продовжуватиму давати вам огляд обох рішень, показуючи послідовність та відмінності. І останнє, але не менш важливе: якщо у вас вже є програма, яка працює з MobX або Redux, я хочу розповісти вам про рефакторинг з однієї в іншу бібліотеку управління станами.
Яку проблему вирішуємо?
Усі хочуть мати керування станом у додатку. Але яку проблему це вирішує нам? Більшість людей починають з невеликої програми і вже впроваджують бібліотеку управління станом. Усі про це говорять, чи не так? Redux! MobX! Але більшість додатків не потребують амбітного управління станами з самого початку. Це ще небезпечніше, бо більшість людей ніколи не зіштовхнуться з тим, які проблеми вирішують такі бібліотеки, як Redux чи MobX.
В даний час статус-кво полягає в побудові фронтенд-додатку з компонентами. Компоненти мають внутрішній стан. У зростаючому додатку управління станом може стати хаотичним з локальним станом, оскільки:
- компонент повинен ділитися станом з іншим компонентом
- компонент повинен видозмінити стан іншого компонента
Рішення, таким чином, полягає в тому, щоб ввести бібліотеку керування станом, таку як MobX або Redux. Вона дає вам інструменти для збереження вашого стану, зміни стану та отримання оновлень стану. У вас є одне місце для пошуку, одне місце для зміни та одне місце для отримання оновлень стану. Він дотримується принципу єдиного джерела правди. Це полегшує міркування щодо змін вашого стану та станів, тому що вони відокремлюються від ваших компонентів.
Бібліотеки управління станом, такі як Redux та MobX, часто мають доповнення до утиліт, наприклад, для Angular вони мають angular-redux та mobx-angular, щоб дати вашим компонентам доступ до стану. Часто ці компоненти називають контейнерними компонентами або, якщо бути точнішим, пов'язаними компонентами. З будь-якої точки вашої ієрархії компонентів ви можете отримати доступ до стану та змінити його, оновивши свій компонент до пов'язаного.
У чому різниця між REDUX та MOBX?
Перед тим, як ми поринемо в різницю, я хочу розповісти вам про схожість між MobX та Redux.
Обидві бібліотеки використовуються для керування станом у JavaScript-додатках. Вони не обов'язково пов'язані з бібліотекою, як Angular. Вони також використовуються в інших бібліотеках, таких як ReactJs та VueJs.
Якщо ви виберете одне з рішень щодо керування станом, ви не зіткнетеся з блокуванням постачальника. Ви можете у будь-який час перейти на інше рішення щодо управління станом. Можна перейти з MobX на Redux або з Redux на MobX. Згодом я продемонструю вам, як це відбувається.
Redux розроблений Деном Абрамовим та Ендрю Кларком є похідним архітектури Flux. На відміну від Flux він використовує одне сховище над декількома для збереження стану. Крім того, замість диспетчера він використовує чисті функції зміни стану. Якщо ви не знайомі з потоком і ви новачок в управлінні станом, не турбуйтеся про останній абзац.
На Redux впливають принципи функціонального програмування (FP). FP може бути зроблено на JavaScript, але багато людей походять з об'єктно-орієнтованого фону, як Java, і мають труднощі у прийнятті принципів функціонального програмування в першу чергу. Пізніше це пояснює, чому MobX може бути легко освоїти як новачок.
Оскільки Redux включає функціональне програмування, він використовує чисті функції.
Чиста функція — функція, яка отримує вхід, повертає вихід і не має інших залежностей, крім таких самих функцій. Така функція робить завжди той самий вихід з тим самим входом і не має жодних побічних ефектів. Докладніше.
