Created
June 12, 2025 12:04
-
-
Save sunmeat/8503dc901e4c65036fa5b41658ce10a0 to your computer and use it in GitHub Desktop.
simple redux example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
App.jsx: | |
// импортируем хуки и компонент Provider из react-redux | |
import {useSelector, useDispatch, Provider} from 'react-redux' | |
// !!! npm install react-redux @reduxjs/toolkit !!! | |
// хук useSelector позволяет получить доступ к состоянию хранилища - единственного центра данных для всего приложения | |
// в хранилище (store) обычно лежит один большой объект — дерево состояния, для всего | |
// useSelector "селектит" (выбирает) нужный кусок данных из этого глобального состояния | |
// и подписывает компонент на обновления выбранной части состояния | |
// компонент будет перерисован, когда выбранное состояние изменится | |
// хук useDispatch возвращает функцию dispatch, с помощью которой можно отправлять действия (actions) в хранилище | |
// через dispatch запускаются изменения состояния, описанные в редьюсерах | |
// считается, что это основной способ взаимодействия компонентов с Redux-состоянием | |
// провайдер это компонент, оборачивающий всё приложение | |
// он делает Redux store доступным для всех вложенных компонентов через контекст | |
// без него хуки useSelector и useDispatch работать не будут, а компоненты не смогут читать или изменять глобальное состояние | |
// это обязательный мост между Redux и React | |
// импортируем функции для создания слайса и хранилища из redux toolkit | |
import {configureStore, createSlice} from '@reduxjs/toolkit' | |
// configureStore - это функция из Redux Toolkit, которая упрощает создание Redux store | |
// это более современная и удобная замена устаревшему в августе 2022 года createStore | |
// она автоматически настраивает devtools, middleware и интеграцию с thunk | |
// devtools - инструмент для отслеживания действий и состояний в браузере | |
// middleware - функции-перехватчики, которые обрабатывают действия между dispatch и reducer | |
// thunk - это разновидность мидлвейр, полезен для написания асинхронных действий (например, API-запросов) | |
// вообще, thunk (глухой звук) - у программистов означает ткусок кода, который выполняет некую отложенную работу https://daveceddia.com/what-is-a-thunk/ | |
// cлайс (slice) — это "кусок" глобального состояния и логики, относящейся к нему | |
// он включает в себя начальное состояние, редьюсеры и сгенерированные экшены | |
// каждый слайс отвечает за свою изолированную часть бизнес-логики (например, счётчик, пользователь, корзина и тд | |
// функция createSlice помогает описать часть состояния (слайс), редьюсеры и экшены одновременно | |
// она создаёт и действия, и редьюсеры автоматически, что сокращает количество кода | |
// это основной способ работы с Redux Toolkit | |
import './App.css' | |
// создаём слайс (кусок состояния) с именем 'counter' | |
// createSlice создаёт объект с двумя важными свойствами: actions и reducer! | |
const counterSlice = createSlice({ | |
name: 'counter', // имя слайса, используется для генерации типов действий | |
initialState: {count: 0}, // начальное состояние счётчика | |
reducers: { | |
// функция-редьюсер для действия increment | |
increment: (state) => { | |
state.count += 1 // увеличиваем значение счётчика | |
}, | |
decrement: (state) => { | |
state.count -= 1 // уменьшаем счётчик на 1 | |
}, | |
}, | |
}) | |
// извлекаем действие increment из созданного слайса | |
const {increment, decrement} = counterSlice.actions | |
// зачем извлекать? основная причина - чистота кода и читаемость | |
// написать далее по коду dispatch(increment()) будет проще, чем dispatch(counterSlice.actions.increment()) | |
// и сразу понятно, что вызывается действие increment | |
// создаём хранилище Redux с редьюсером из слайса | |
const store = configureStore({ | |
reducer: counterSlice.reducer, // подключаем редьюсер к хранилищу | |
}) | |
// createSlice упаковывает отдельные обработчики действий из reducers в одну функцию counterSlice.reducer | |
// если несколько слайсов, то будет reducer: { | |
// counter: counterSlice.reducer, | |
// anotherSlice: anotherSlice.reducer, | |
// } | |
// компонент, в котором используется состояние и действия Redux | |
function Counter() { | |
const count = useSelector((state) => state.count) // получаем значение счётчика из хранилища | |
const dispatch = useDispatch() // получаем функцию для отправки действий | |
return ( | |
<> | |
<h1>Redux Toolkit</h1> | |
<div className="card"> | |
<button onClick={() => dispatch(decrement())}> | |
decrement | |
</button> | |
<button onClick={() => dispatch(increment())}> | |
increment | |
</button> | |
<p>count is {count}</p> | |
</div> | |
</> | |
) | |
} | |
// тут обязательно оборачиваем Counter в Provider | |
function App() { | |
return ( | |
<Provider store={store}> {/* передаём хранилище всем дочерним компонентам */} | |
<Counter/> | |
</Provider> | |
) | |
} | |
// redux — это централизованное хранилище состояния приложения, | |
// и чтобы компоненты могли получить доступ к этому состоянию или изменить его, им нужно знать, где этот store | |
// напрямую прокидывать store через пропсы во все компоненты | |
// - это боль и трата времени, особенно если компонентов много и они вложены глубоко | |
// поэтому React Redux использует контекст (React Context API), чтобы «прокинуть» store на самый верх | |
// и дать возможность любому вложенному компоненту получить к нему доступ не зависимо от вложенности | |
// провайдер принимает store в пропсе и через контекст делает это хранилище доступным для всех потомков | |
// ни один компонент не получает store напрямую через пропсы — вместо этого они «подписываются» на контекст | |
// и с помощью хуков useSelector и useDispatch работают с хранилищем | |
// кто "ждёт" этот store? - все компоненты, которые используют: | |
// useSelector — чтобы выбрать часть состояния из store | |
// useDispatch — чтобы отправлять действия (actions) в store | |
// под капотом эти хуки обращаются к React Context, созданному <Provider>, чтобы получить экземпляр store | |
// без <Provider> хуки просто не смогут найти и использовать хранилище, и приложение сломается | |
export default App | |
========================================================================================================== | |
App.css: | |
body { | |
margin: 0; | |
padding: 0; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
background: linear-gradient(135deg, #667eea, #764ba2); | |
color: #f0f0f0; | |
height: 100vh; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
h1 { | |
text-align: center; | |
margin-bottom: 1rem; | |
font-weight: 700; | |
letter-spacing: 2px; | |
text-shadow: 0 0 8px rgba(255, 255, 255, 0.7); | |
} | |
.card { | |
background: rgba(255, 255, 255, 0.1); | |
padding: 2rem 3rem; | |
border-radius: 15px; | |
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4); | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
gap: 1.5rem; | |
min-width: 320px; | |
} | |
button { | |
background: #764ba2; | |
color: #fff; | |
border: none; | |
padding: 0.6rem 1.5rem; | |
font-size: 1.1rem; | |
border-radius: 8px; | |
cursor: pointer; | |
font-weight: 600; | |
transition: background 0.3s ease, transform 0.15s ease; | |
box-shadow: 0 4px 12px rgba(118, 75, 162, 0.5); | |
user-select: none; | |
} | |
button:hover { | |
background: #5e3880; | |
transform: scale(1.05); | |
box-shadow: 0 6px 18px rgba(94, 56, 128, 0.7); | |
} | |
button:active { | |
transform: scale(0.95); | |
box-shadow: 0 3px 10px rgba(94, 56, 128, 0.6); | |
} | |
p { | |
font-size: 1.3rem; | |
font-weight: 700; | |
letter-spacing: 1px; | |
margin: 0; | |
text-shadow: 0 0 6px rgba(255, 255, 255, 0.6); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment