0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Всем привет!</p>
1
<p>Всем привет!</p>
2
<p>Этой мой первый пост в дневнике, и посвящён он библиотеке Redux.</p>
2
<p>Этой мой первый пост в дневнике, и посвящён он библиотеке Redux.</p>
3
<p>За время моего обучения на Hexlet именно эта библиотека породила кучу вопросов а-ля -'как это работает, почему это так работает ?'. Несколько дней я перечитывал теорию по Redux, но в голове не укладывалось, пока я не написал свою собственную версию Redux.</p>
3
<p>За время моего обучения на Hexlet именно эта библиотека породила кучу вопросов а-ля -'как это работает, почему это так работает ?'. Несколько дней я перечитывал теорию по Redux, но в голове не укладывалось, пока я не написал свою собственную версию Redux.</p>
4
<p>Предлагаю вам вместе со мной написать свою версию библиотеки, просто чтобы разобраться как это работает "под капотом".</p>
4
<p>Предлагаю вам вместе со мной написать свою версию библиотеки, просто чтобы разобраться как это работает "под капотом".</p>
5
<p>В Redux ядром всего является объект store, который возвращает нам функция createStore(). Поэтому давайте начнём с создания этой функции.</p>
5
<p>В Redux ядром всего является объект store, который возвращает нам функция createStore(). Поэтому давайте начнём с создания этой функции.</p>
6
<blockquote><p>Согласно документации createStore принимает первым аргументов функцию reducer, а вторым начальное состояние.</p>
6
<blockquote><p>Согласно документации createStore принимает первым аргументов функцию reducer, а вторым начальное состояние.</p>
7
</blockquote><p>const createStore = (reducer, initialState) => { let state = reducer(initialState, { type: '__INIT__' }); let subscribers = []; return { dispatch(action) { state = reducer(state, action); subscribers.forEach((cb) => cb()); }, subscribe(cb) { subscribers = [...subscribers, cb]; }, getState() { return state; }, }; };</p>
7
</blockquote><p>const createStore = (reducer, initialState) => { let state = reducer(initialState, { type: '__INIT__' }); let subscribers = []; return { dispatch(action) { state = reducer(state, action); subscribers.forEach((cb) => cb()); }, subscribe(cb) { subscribers = [...subscribers, cb]; }, getState() { return state; }, }; };</p>
8
<blockquote><p>Сначала инициализируем state начальными данными.</p>
8
<blockquote><p>Сначала инициализируем state начальными данными.</p>
9
</blockquote><blockquote><p>let state = reducer(initialState, { type: '__INIT__' });</p>
9
</blockquote><blockquote><p>let state = reducer(initialState, { type: '__INIT__' });</p>
10
</blockquote><blockquote><p>Функцию reducer мы напишем позже, но главное понимать, что функция возвращает измененный или неизменённый state.</p>
10
</blockquote><blockquote><p>Функцию reducer мы напишем позже, но главное понимать, что функция возвращает измененный или неизменённый state.</p>
11
</blockquote><blockquote><p>Функция createStore должна вернуть объект store со следующими методами: dispatch(), subscribe(), getState().</p>
11
</blockquote><blockquote><p>Функция createStore должна вернуть объект store со следующими методами: dispatch(), subscribe(), getState().</p>
12
</blockquote><blockquote><p>getState() просто возвращает текущий state.</p>
12
</blockquote><blockquote><p>getState() просто возвращает текущий state.</p>
13
</blockquote><blockquote><p>subscribe() принимает в качестве аргумента callback-функцию и складывает функцию в массив.</p>
13
</blockquote><blockquote><p>subscribe() принимает в качестве аргумента callback-функцию и складывает функцию в массив.</p>
14
</blockquote><blockquote><p>dispatch() принимает в качестве аргумента объект. Этот объект может иметь внутри любые поля и данные, но в обязательном порядке должен иметь поле type. Например { type: 'ADD' }. Когда вызывается метод dispatch(), то state должен быть изменен через reducer. Reducer - это функция, внутри которой данные меняются, в зависимости от описанной внутри логики. После того, как state был изменен, нам нужно последовательно вызвать все callback-функции, которые были добавлены в массив посредством метода subscribe().</p>
14
</blockquote><blockquote><p>dispatch() принимает в качестве аргумента объект. Этот объект может иметь внутри любые поля и данные, но в обязательном порядке должен иметь поле type. Например { type: 'ADD' }. Когда вызывается метод dispatch(), то state должен быть изменен через reducer. Reducer - это функция, внутри которой данные меняются, в зависимости от описанной внутри логики. После того, как state был изменен, нам нужно последовательно вызвать все callback-функции, которые были добавлены в массив посредством метода subscribe().</p>
15
</blockquote><p>Теперь создаем функцию reducer, которая согласно внутренней логики будет менять состояние.</p>
15
</blockquote><p>Теперь создаем функцию reducer, которая согласно внутренней логики будет менять состояние.</p>
16
<p>const ourReducer = (state, action) => { switch(action.type) { case 'INC': return state + 1; case 'DEC': return state - 1; default: return state; } };</p>
16
<p>const ourReducer = (state, action) => { switch(action.type) { case 'INC': return state + 1; case 'DEC': return state - 1; default: return state; } };</p>
17
<blockquote><p>Здесь все должно быть понятно. Функция принимает объект action с полем type. Например { type: 'INC }. В зависимости от значения поля type отрабатывает switch-case конструкция и происходит возврат состояния.</p>
17
<blockquote><p>Здесь все должно быть понятно. Функция принимает объект action с полем type. Например { type: 'INC }. В зависимости от значения поля type отрабатывает switch-case конструкция и происходит возврат состояния.</p>
18
</blockquote><p>Дальше создаем объект store, интерфейсами которого и будем манипулировать.</p>
18
</blockquote><p>Дальше создаем объект store, интерфейсами которого и будем манипулировать.</p>
19
<p>const store = createStore(ourReducer, 0);</p>
19
<p>const store = createStore(ourReducer, 0);</p>
20
<p>Теперь попробуем вызвать методы нашего хранилища store. Для начала мы хотим чтобы при изменении нашего состояния в console автоматически печаталось текущее состояние.</p>
20
<p>Теперь попробуем вызвать методы нашего хранилища store. Для начала мы хотим чтобы при изменении нашего состояния в console автоматически печаталось текущее состояние.</p>
21
<p>store.subscribe(() => console.log(store.getState()));</p>
21
<p>store.subscribe(() => console.log(store.getState()));</p>
22
<p>Теперь вернем текущее состояние</p>
22
<p>Теперь вернем текущее состояние</p>
23
<p>store.getState(); // -> вернётся 0, так как это начальное состояние.</p>
23
<p>store.getState(); // -> вернётся 0, так как это начальное состояние.</p>
24
<p>Теперь изменим наше состояние методом dispatch(), который внутри вызывает редьюсер, меняющий состояние, и вызывает последовательно все callback функции из массива subscribers.</p>
24
<p>Теперь изменим наше состояние методом dispatch(), который внутри вызывает редьюсер, меняющий состояние, и вызывает последовательно все callback функции из массива subscribers.</p>
25
<p>store.dispatch({ type: 'INC' }); // -> в консоле будет 1 store.dispatch({ type: 'INC' }); // -> в консоле будет 2 store.dispatch({ type: 'DEC' }); // -> в консоле будет 1</p>
25
<p>store.dispatch({ type: 'INC' }); // -> в консоле будет 1 store.dispatch({ type: 'INC' }); // -> в консоле будет 2 store.dispatch({ type: 'DEC' }); // -> в консоле будет 1</p>
26
<p>Как оказалось, ничего сложного здесь нет. Надеюсь мои эксперименты кому-то помогут быстрее вникнуть в то, как работает библиотека Redux.</p>
26
<p>Как оказалось, ничего сложного здесь нет. Надеюсь мои эксперименты кому-то помогут быстрее вникнуть в то, как работает библиотека Redux.</p>
27
<blockquote><p><em>Ниже представлен полный код для удобства копирования в свой редактор:</em></p>
27
<blockquote><p><em>Ниже представлен полный код для удобства копирования в свой редактор:</em></p>
28
</blockquote><p>const createStore = (reducer, initialState) => { let state = reducer(initialState, { type: '__INIT__' }); let subscribers = []; return { dispatch(action) { state = reducer(state, action); subscribers.forEach((cb) => cb()); }, subscribe(cb) { subscribers = [...subscribers, cb]; }, getState() { return state; }, }; }; const ourReducer = (state, action) => { switch(action.type) { case 'INC': return state + 1; case 'DEC': return state - 1; default: return state; } }; const store = createStore(ourReducer, 0); store.subscribe(() => console.log(store.getState())); store.getState(); store.dispatch({ type: 'INC' }); store.dispatch({ type: 'INC' }); store.dispatch({ type: 'DEC' });</p>
28
</blockquote><p>const createStore = (reducer, initialState) => { let state = reducer(initialState, { type: '__INIT__' }); let subscribers = []; return { dispatch(action) { state = reducer(state, action); subscribers.forEach((cb) => cb()); }, subscribe(cb) { subscribers = [...subscribers, cb]; }, getState() { return state; }, }; }; const ourReducer = (state, action) => { switch(action.type) { case 'INC': return state + 1; case 'DEC': return state - 1; default: return state; } }; const store = createStore(ourReducer, 0); store.subscribe(() => console.log(store.getState())); store.getState(); store.dispatch({ type: 'INC' }); store.dispatch({ type: 'INC' }); store.dispatch({ type: 'DEC' });</p>