Redux. Простой как грабли
Мне уже доводилось заглядывать в репозиторий библиотеки redux, но откуда-то появилась мысль углубиться в его реализацию. Своим в некотором роде шокирующим или даже разочаровывающим открытием я хотел бы поделиться с сообществом.
TL;DR: базовая логика redux помещается в 7 строк JS кода.
О redux вкратце (вольный перевод заголовка на гитхабе):
Redux — библиотека управления состоянием для приложений, написанных на JavaScript.
Она помогает писать приложения, которые ведут себя стабильно/предсказуемо, работают на разных окружениях (клиент/сервер/нативный код) и легко тестируемы.
Я склонировал репозиторий redux, открыл в редакторе папку с исходниками (игнорируя docs, examples и прочее) и взялся за ножницы клавишу Delete:
- Удалил все комментарии из кода
Каждый метод библиотеки задокументирован с помощью JSDoc весьма подробно - Убрал валидацию и логирование ошибок
В каждом методе жёстко контролируются входные параметры с выведением очень приятных глазу подробных комментариев в консоль - Убрал методы bindActionCreators, subscribe, replaceReducer и observable.
Пишем redux за 7 строк
Весь базовый функционал redux умещается в малюсенький файлик, ради которого вряд ли кто-нибудь будет создавать github репозиторий 🙂
function createStore(reducer, initialState) < let state = initialState return < dispatch: action =>< state = reducer(state, action) >, getState: () => state, > >
Всё. Да, серьёзно, ВСЁ.
Так устроен redux. 18 страниц вакансий на HeadHunter с поисковым запросом «redux» — люди, которые надеются, что вы разберетесь в 7 строках кода. Всё остальное — синтаксический сахар.
С этими 7 строками уже можно писать TodoApp. Или что угодно. Но мы быстренько перепишем TodoApp из документации к redux.
// Инициализация хранилища function todosReducer(state, action) < switch (action.type) < case 'ADD_TODO': return [ . state, < id: action.id, text: action.text, completed: false >] case 'TOGGLE_TODO': return state.map(todo => < if (todo.id === action.id) < return < . todo, completed: !todo.completed >> return todo >) default: return state > > const initialTodos = [] const store = createStore(todosReducer, initialTodos) // Использование store.dispatch(< type: 'ADD_TODO', id: 1, text: 'Понять насколько redux прост' >) store.getState() // [< id: 1, text: 'Понять насколько redux прост', completed: false >] store.dispatch(< type: 'TOGGLE_TODO', id: 1 >) store.getState() // [< id: 1, text: 'Понять насколько redux прост', completed: true >]
Уже на этом этапе я думал бросить микрофон со сцены и уйти, но show must go on.
Давайте посмотрим, как устроен метод.
combineReducers
Это метод, который позволяет вместо того, чтобы создавать один огромный reducer для всего состояния приложения сразу, разбивать его на отдельные модули.
Используется он так:
// здесь мы переиспользуем метод todosReducer из прошлого примера function counterReducer(state, action) < if (action.type === 'ADD') < return state + 1 >else < return state >> const reducer = combineReducers(< todoState: todoReducer, counterState: counterReducer >) const initialState = < todoState: [], counterState: 0, >const store = createStore(reducer, initialState)
Дальше использовать этот store можно так же, как предыдущий.
Разница моего примера и описанного в той же документации к TodoApp довольно забавная.
В документации используют модный синтаксис из ES6 (7/8/∞):
const reducer = combineReducers(< todos, counter >)
и соответственно переименовывают todoReducer в todos и counterReducer в counter. И многие в своём коде делают то же самое. В итоге разницы нет, но для человека, знакомящегося с redux, с первого раза эта штука выглядит магией, потому что ключ части состояния (state.todos) соответствует функции, названной также только по желанию разработчика (function todos()<>).
Если бы нам нужно было написать такой функционал на нашем micro-redux, мы бы сделали так:
function reducer(state, action) < return < todoState: todoReducer(state, action), counterState: counterReducer(state, action), >>
Этот код плохо масштабируется. Если у нас 2 «под-состояния», нам нужно дважды написать (state, action), а хорошие программисты так не делают, правда?
В следующем примере от вас ожидается, что вы не испугаетесь метода Object.entries и Деструктуризации параметров функции
Однако реализация метода combineReducers довольно простая (напоминаю, это если убрать валидацию и вывод ошибок) и самую малость отрефакторить на свой вкус:
function combineReducers(reducersMap) < return function combinationReducer(state, action) < const nextState = <>Object.entries(reducersMap).forEach(([key, reducer]) => < nextState[key] = reducer(state[key], action) >) return nextState > >
Мы добавили к нашему детёнышу redux ещё 9 строк и массу удобства.
Перейдём к ещё одной важной фиче, которая кажется слишком сложной, чтобы пройти мимо неё.
applyMiddleware
middleware в разрезе redux — это какая-то штука, которая слушает все dispatch и при определенных условиях делает что-то. Логирует, проигрывает звуки, делает запросы к серверу,… — что-то.
В оригинальном коде middleware передаются как дополнительные параметры в createStore, но если не жалеть лишнюю строчку кода, то использование этого функционала выглядит так:
const createStoreWithMiddleware = applyMiddleware(someMiddleware)(createStore) const store = createStoreWithMiddleware(reducer, initialState)
При этом реализация метода applyMiddleware, когда ты потратишь 10 минут на ковыряние в чужом коде, сводится к очень простой вещи: createStore возвращает объект с полем «dispatch». dispatch, как мы помним (не помним) из первого листинга кода, — это функция, которая всего лишь применяет редюсер к нашему текущему состоянию (newState = reducer(state, action)).
Так вот applyMiddleware не более чем переопределяет метод dispatch, добавляя перед (или после) обновлением состояния какую-то пользовательскую логику.
Возьмём, например, самый популярный middleware от создателей redux — redux-thunk
Его смысл сводится к тому, что можно делать не только
store.dispatch()
но и передавать в store.dispatch сложные функции
function someStrangeAction() < return async function(dispatch, getState) < if(getState().counterState % 2) < dispatch(< type: 'ADD', >) > await new Promise(resolve => setTimeout(resolve, 1000)) dispatch(< type: 'TOGGLE_TODO', id: 1 >) > >
И теперь, когда мы выполним команду
dispatch(someStrangeAction())
- если значение store.getState().counterState не делится на 2, оно увеличится на 1
- через секунду после вызова нашего метода, todo с переключит completed true на false или наоборот.
const thunk = store => dispatch => action => < if (typeof action === 'function') < return action(store.dispatch, store.getState) >return dispatch(action) >
я понимаю, что конструкция
const thunk = store => dispatch => action
выглядит жутковато, но её тоже просто нужно вызвать пару раз с произвольными параметрами и вы осознаете, что всё не так страшно, это просто функция, возвращающая функцию, возвращающую функцию (ладно, согласен, страшно)
Напомню, оригинальный метод createStore выглядел так
function createStore(reducer, initialState) < let state = initialState return < dispatch: action =>< state = reducer(state, action) >, getState: () => state, > >
То есть он принимал атрибуты (reducer, initialState) и возвращал объект с ключами < dispatch, getState >.
Оказалось, что реализовать метод applyMiddleware проще, чем понять, как он работает.
Мы берём уже реализованный метод createStore и переопределяем его возвращаемое значение:
function applyMiddleware(middleware) < return function createStoreWithMiddleware(createStore) < return (reducer, state) => < const store = createStore(reducer, state) return < dispatch: action =>middleware(store)(store.dispatch)(action), getState: store.getState, > > > >
Вывод
Под капотом redux содержатся очень простые логические операции. Операции на уровне «Если бензин в цилиндре загорается, давление увеличивается». А вот то, сможете ли вы построить на этих понятиях болид Формулы 1 — уже решайте сами.
P.S.
Для добавления в мой «micro-redux» упрощённого метода store.subscribe потребовалось 8 строк кода. А вам?
- redux
- javascript
- frontend-разработка
Как подключить Redux Framework в свой плагин
Redux Framework — это удобный и расширяемый фреймворк для создания страниц настроек тем и плагинов для WordPress.
Подходит для случаев, когда настроек очень много, для простых же ситуаций лучше использовать нативный Settings API .
На данный момент имеется уже четвертая версия Redux Framework, которая находится на стадии бета-тестирования , вот ее мы и возьмем за основу, так как эта версия фреймворка стала значительно быстрее и полностью соответствует стандартам WPCS .
Для начала добавим Redux Framework как зависимость к нашему проекту в composer.json :
"require": < "composer/installers": "*", "reduxframework/redux-framework-4" : "*" >,
Выполним установку зависимостей:
composer install
И обнаружим, что фреймворк установился не в папку vendor , как мы расчитывали, а в wp-content/plugins , которая создалась в корне вашего проекта:

Произошло так потому, что у самого Redux Framework в его composer.json укзан тип проекта «type»: «wordpress-plugins».
Если вам хочется иметь фреймворк в папке vendor , то придётся немного пошаманить с путями в вашем файле настроек.
Так как плагина Redux Framework 4 пока нет в Packagist , укажем композеру, откуда выкачивать пакет:
"repositories": [ < "type": "package", "package": < "name": "reduxframework/redux-framework-4", "type": "wordpress-plugin", "version": "4.0.1", "dist": < "type": "zip", "url": "https://github.com/reduxframework/redux-framework-4/archive/master.zip" >, "require" : < "composer/installers": "*" >> > ]
Укажем, куда устанавливать пакеты с типом wordpress-plugins :
"extra": < "installer-paths": < "vendor//": ["type:wordpress-plugin"] > >
Обычно классы пакетов, которые устанавливаются в папку vendor , не нуждаются в ручном подключении и меппинге, но в Redux Framework не всё как у людей. Поэтому добавим папку с его классами ручками в блок autoload :
"autoload": < "classmap": [ "includes", "vendor/redux-framework-4/ReduxCore" ] >
Снова выполним установку пакета, прдварительно удалив папку wp-content :
rm -fr wp-content composer install
На этом процесс конфигурации композера можно считать завершённым. Пробуем:
Redux::get_option( 'option_name' );
Полную версию composer.json с подключенным Redux Framework вы можете подглядеть в моем плагине для Турбо страниц .
Удачи. Всё будет WordPress!
Как отключить режим разработки в Redux Framework
В Redux Framework при активной опции WP_DEBUG = true выводится новостной информер в консоли WordPress, баннер на странице настроек вашего плагина или темы и уведомление админу, что сайт находится в режиме разработки.
Также в левом меню админки в Инструментах всегда болтается ссылка на страницу диагностики Redux Framework.
Чтобы удалить все следы использования фреймворка от пользователя и не пугать его лишними меню и баннерами, если у него случайно окажется включённым дебаг, предлагаю использовать простой сниппет:
/** * Отключаем режима разработки * * @param ReduxFramework $redux */ function mihdan_redux_disable_dev_mode( ReduxFramework $redux ) < $redux->args['dev_mode'] = false; $redux->args['forced_dev_mode_off'] = false; > add_action( 'redux/construct', 'mihdan_redux_disable_dev_mode' ); /** * Удаляем меню Redux из Инструментов * * @param string $page * @param Redux_Welcome $welcome */ function mihdan_redux_remove_tools_menu( $page, Redux_Welcome $welcome ) < remove_submenu_page( 'tools.php', 'redux-framework' ); >add_action( 'redux/pro/welcome/admin/menu', 'mihdan_redux_remove_tools_menu', 10, 2 );
Если вы боитесь лезть в код, то специально для таких случаев на официальном сайте проекта есть платное расширение Ad Remover за 60$, которое сделает всё это за вас.
Sharing is caring
Понравилось это:
Нравится Загрузка.
Похожее
Рубрики Статьи Метки PHP
Суровый русский тимлид. Жил в Магадане, в офисе московских веб студий и в Тульской деревне. Виртуозно знает WordPress, PHP, ООП, Vue.js и вот это вот все. Делает крутые высоконагруженные сайты, поэтому уже почти захватил весь рынок WordPress разработки в России. Не дает никому делать сайты без спроса. Ведет блог о разработке, дайджест в телеграмме и в ВК. Любит сиськи, баню и радиоэлектронику. 100% патриот (но это не точно). Тролль 542 уровня. Ездит в отпуск раз в 5 лет.
Добавить комментарийОтменить ответ
О сайте
Самое полное собрание обучающих материалов для пользователей и разработчиков WordPress. Гарантированное качество и доступность в любое время!
Навигация
- Новости WordPress (RSS)
- Навигация по сайту
- О проекте
- Авторы
- Стать автором
- Обратная связь
- Ссылки
Redux Framework – Отличное решение для быстрого создания красивых и функциональных плагинов и тем WordPress

Redux Framework – одна из самых популярных, продвинутых и бесплатных платформ опционных панелей для тем и плагинов WordPress. Его гибкость дает вам свободу создавать любые типы опций и настроек для вашего проекта на WordPress.
В этом уроке я покажу вам, как вы можете установить, настроить и использовать Redux framework в своей собственной теме.
Мы пройдем через следующие шаги:
- Установка
- Общая конфигурация
- Создание панели параметров
- Использование параметров в теме
Примечание : я буду ссылаться на Redux Framework просто как Redux на протяжении всего этого урока. Это не следует путать с Redux , «контейнером предсказуемого состояния для приложений JavaScript».
Установка
Есть три способа включить Redux в ваш проект:
- Установить его как отдельный плагин
- Включить его в ядро ??вашей темы
- Включить его в зависимый от темы плагин
Установка отдельным плагином
По умолчанию Redux может быть установлен как плагин. Если вы перейдете на страницу плагинов Redux Framework WordPress, то увидите, что вы можете скачать его как обычный плагин и установить на свой сайт WordPress с любой темой. Это не создает никаких параметров, хотя все, что вы можете сделать, это активировать демонстрационный режим с примерами параметров.
Чтобы получить панель с настраиваемыми параметрами для вашей темы или плагина, вам нужно создать отдельный файл config.php.
Отсюда плюсы и минусы этого подхода:
Плюсы
- Отдельная установка плагина, которую необходимо сохранить в виде zip-файла на вашем сервере или в папке темы.
- Вы уменьшаете размер своей темы, когда панель параметров не является частью ядра темы.
Минусы
- Усилилось ощущение, что тема / плагин зависит от сторонних разработок и инструментов.
- Вы избегаете многочисленных предупреждений и ошибок от плагина Theme Check .
Включение в ядро темы
Ничто не остановит вас, включая Redux в вашей теме, если только он не конфликтует с плагином Theme Check. Логика та же, что и раньше, вам все еще нужен файл config.php с вашими пользовательскими настройками, но теперь вы избегаете установки дополнительных плагинов.
Минусы
Увеличивает размер пакета темы.
Избегайте установки дополнительных плагинов.
Потенциальные конфликты и проблемы могут возникнуть из-за плагина Theme Check.
Панель параметров темы чувствует себя более связанной с вашей темой и более интегрированной.
Всякий раз, когда плагин обновляется, вам нужно обновить свою тему тоже.
Включение в зависимый от темы плагин
На мой взгляд, такой подход – лучший способ включить фреймворк Redux. Процесс такой же простой, как и обычная установка плагина, с той лишь разницей, что теперь он является частью необходимого дополнительного плагина вашей темы.
Современные премиальные темы обычно поставляются с пользовательскими элементами – пользовательскими типами сообщений и т.д. – все они принадлежат плагину, поэтому логичным подходом является создание дополнительного плагина, связанного с вашей темой. В этот плагин вы можете включить панель настроек темы.
Минусы
Не нашел
Вы избегайте установки дополнительных плагинов.
Панель параметров темы кажется более интегрированной в вашу тему.
На процесс настройки Redux не влияет метод установки, поэтому вам решать, как вы хотите включить Redux в свой проект. Тем не менее, я все еще предпочитаю последний вариант, поэтому я буду использовать этот подход для описания конфигурации.
Общая конфигурация
Если вы изучите папку плагина Redux Framework по умолчанию, вы найдете множество файлов и каталогов, но все, что вам здесь нужно, это папка ReduxCore.

Скопируйте все содержимое этой папки в папку с надстройками. Создайте папку, назовите ее как хотите, например, optionpanel, и вставьте в нее содержимое ReduxCore. Затем создайте пустой файл внутри этой папки и назовите его config.php. Далее нам нужно будет подключиться к reduxframework , в бесконфликтном режиме, поэтому, если плагин надстройки установлен в среде WordPress, сайт не будет “падать”.
В основной файл вашего плагина добавьте код:
if (!class_exists('ReduxFramework') & file_exists(plugin_dir_path(__FILE__) . '/optionpanel/framework.php')) < require_once ('optionpanel/framework.php'); > if (!isset($redux_demo) & file_exists(plugin_dir_path(__FILE__) . '/optionpanel/config.php')) < require_once ('optionpanel/config.php'); > -->
Вот и все! Redux Framework теперь интегрирован, хотя вы еще не увидите панели параметров, так как сначала нам нужно создать некоторые параметры. Все это будет сделано в файле config.php, который мы создали.
Создание панели параметров
В качестве руководства и отправной точки вы можете использовать файл sample-config.php, поставляемый с плагином Redux. Он содержит весь код, необходимый для создания собственных пользовательских параметров. А пока давайте выделим основной процесс настройки.
Откройте файл config.php и в начале добавьте этот код (открывающий тег не понадобится, если он уже присутствует):
if (!class_exists('Redux')) < return; >
Эта проверка гарантирует, что Redux Framework активен, в противном случае все созданные нами параметры не будут работать и, скорее всего, приведут к множеству ошибок PHP.
Прежде чем мы начнем создавать наши опции, нам сначала понадобится дополнительная настройка. Создайте переменную, которая будет хранить все ваши параметры и может использоваться в файлах вашей темы:
$opt_name = "your_variable_name";
Сделайте её уникальной, с помощью префиксов, например: yourbrandname_yourthemename .
После этого добавьте код:
$theme = wp_get_theme();
Это необходимо для настройки любых аргументов Redux, которые используют информацию об установленной теме.
Аргументы
На данный момент нам нужно добавить следующие аргументы:
$args = array( 'opt_name' => $opt_name, 'display_name' => $theme->get('Name') , 'display_version' => $theme->get('Version') , 'menu_type' => 'submenu', 'allow_sub_menu' => true, 'menu_title' => esc_html__('Theme Settings', yourtextdomain'),'page_title' => esc_html__('ThemeSettings', yourtextdomain') , 'google_api_key' => '', 'google_update_weekly' => false, 'async_typography' => true, 'admin_bar' => true, 'admin_bar_icon' => '', 'admin_bar_priority' => 50, 'global_variable' => $opt_name, 'dev_mode' => false, 'update_notice' => false, 'customizer' => true, 'page_priority' => null, 'page_parent' => 'themes.php', 'page_permissions' => 'manage_options', 'menu_icon' => '', 'last_tab' => '', 'page_icon' => 'icon-themes', 'page_slug' => 'themeoptions', 'save_defaults' => true, 'default_show' => false, 'default_mark' => '', 'show_import_export' => true );
Здесь много аргументов, но не волнуйтесь, я выделю самые важные.
Если вы хотите, чтобы страница параметров вашей темы отображалась как отдельный элемент меню верхнего уровня, вам следует установить это значение menu , если вам нужно, чтобы параметры вашей темы отображались в виде подменю (я предпочитаю размещать их в разделе Внешний вид ), установите значение на submenu .
Вы можете включить это во время разработки, но не забудьте отключить это при публикации темы.
Поскольку Redux будет частью вашего проекта, вы не захотите, чтобы ваши пользователи получали какие-либо уведомления об обновлениях от Redux Framework, поэтому установите это значение false .
Если вы хотите показать свои пользовательские настройки как часть настройщика WordPress, просто установите для этого аргумента значение true .
Массив arguments содержит гораздо больше элементов, и если вам нужна информация о каждом из них, вы можете найти подробности в файле sample-config.php. Для наших нужд мы настроили аргументы достаточно. Теперь давайте установим аргументы.
Redux ::setArgs( $opt_name , $args );
Добавление разделов
Теперь ваша панель темы готова, вы можете добавлять разделы с опциями. Думайте о секциях как о группах. Если вы хотите добавить (например) параметры заголовка, вы создаете раздел заголовка и добавляете в него параметры. Для создания любого раздела вам понадобится следующая структура:
Redux::setSection($opt_name, array( 'title' => esc_html__('Section title', 'yourtextdomain') , 'id' => esc_html__('section-unique-id', ' yourtextdomain') , 'icon' => 'icon-name', 'fields' => array()));
Здесь я хочу выделить id атрибут – убедитесь, что он уникален. Вы можете использовать пользовательские значки, но набор значков по умолчанию – elusiveicons .
Ваш раздел создан, так что теперь вы можете добавлять опции в массив полей. Список доступных типов опций, а также структуру кода с пояснениями и основными моментами можно найти здесь .
Если вы хотите сделать раздел подразделом ранее добавленного раздела, просто добавьте новый аргумент ‘subsection’ => true .
Сам параметр – это не что иное, как массив с аргументами, например:
array( 'id' => 'opt-checkbox', 'type' => 'checkbox', 'title' => esc_html__('Checkbox', 'yourtextdomain') , 'subtitle' => esc_html__('No validation can be done on this field type', 'yourtextdomain') , 'desc' => esc_html__('This is the description field, again good for additional info.', ' yourtextdomain') , 'default' => '1' // 1 = on | 0 = off),
С помощью этого кода мы добавили флажок в наш раздел. Наиболее важными здесь являются идентификатор (он должен быть уникальным) и тип (он должен быть правильным и следовать точному именованию типа поля, предоставляемого Redux). Опять же, более подробную информацию о полях можно найти в файле sample-config.php.
Как правило, это все, что вам нужно знать, чтобы создать панель параметров. Далее мы рассмотрим, как использовать эти параметры.
Использование параметров в теме
Redux хранит все параметры в глобальной переменной. Помните $opt_name переменную, которую мы создали в файле config.php? Это переменная, которая хранит все ваши параметры, и вы можете получить к ним доступ следующим образом.
Во-первых, вам нужно объявить глобальную переменную. И это представляет первую потенциальную проблему: не рекомендуется объявлять глобальную переменную вне функции или действия. Решение состоит в том, чтобы добавить в ваш файл functions.php эту небольшую функцию:
function yourprefix_theme_options_global_variable() < global $yourglobalvariable; >
Затем на каждой странице, в которой вы хотите использовать свои параметры, сначала выполните функцию следующим образом:
yourprefix_theme_options_global_variable ();
Если вам нужна глобальная переменная внутри другой функции или действия, вы можете объявить ее без этой функции.
Использование параметров
Если вы используете опции без предварительной проверки, существует ли эта опция, вы получите уведомление PHP, в котором говорится о неопределенной переменной / индексе . Почему это важно? Потому что, если пользователь активирует вашу тему, но не активирует дополнительный плагин, содержащий вашу панель параметров, он или она увидит коллекцию предупреждений PHP о неопределенных переменных. Не идеально.
Существует хорошая схема, позволяющая избежать этой ситуации – она ??выглядит так:
$your_option_name = (isset($GLOBALS['yourglobalvariable']['youroption'])) ? $GLOBALS['yourglobalvariable']['youroption'] : “yourdefaultvalue for thisoption”;
Эта небольшая проверка гарантирует, что вы не получите никаких неопределенных уведомлений о переменных / индексах. Если вы не хотите назначать значения по умолчанию или создавать переменные, вы можете использовать этот шаблон:
If (isset($GLOBALS['yourglobalvariable']['youroption']) < // do the stuff…>
Вывод
Спасибо за чтение, я надеюсь, что это поможет вам начать использовать Redux для вашей темы! Если у вас есть какие-либо вопросы или предложения, пожалуйста, оставьте комментарий ниже.
Полезные ссылки
- reduxframework.com
- Плагин для WordPress
- Блог Redux
- Начало работы с Redux Framework.
- ReduxFramework на Facebook
- @ReduxFramework в Твиттере
Статья была переведена для блога TechBlog.SDStudio.top