Как стать автором
Обновить

Redux Action Creators. Без констант и головной боли

Время на прочтение3 мин
Количество просмотров45K


Всем привет! Эта статья будет полезна тем, кто устал использовать constants в Redux (частично показано на превью выше). Под катом я покажу очередной возможный велосипед и как на нем кататься.




Модуль + документация (https://github.com/pavelivanov/redaction)



Введение


Использование Redux предполагает наличие экшнов (actions) и редьюсеров (reducers), а также констант (constants), которые используются для связи экшнов с редьюсерами посредством передачи type (типа экшна).


Пример использования:


const ADD_TODO = 'ADD_TODO'

export {
  ADD_TODO
}

import { ADD_TODO } from 'constants'

export const addTODO = () => {
  return (dispatch) => {
    dispatch({
      type: ADD_TODO,
      item
    })
  }
}

const ADD_TODO = 'ADD_TODO'

const initialState = {
  TODO: []
}

export default (state = initialState, action) => {
  switch (action.type) {

    case 'ADD_TODO':
      return {
        ...state,
        TODO: [
          ...state.TODO,
          action.TODO
        ]
      }

    default:
      return state
  }
}

В таком подходе немало минусов, начиная от использования констант, и заканчивая пробрасыванием dispatch метода во все компоненты, где нам необходимо вызвать экшн.


Простые Reducers


В Redaction я избавился от этих проблем. Все что вам нужно — это создать экшн и использовать его. Все.


Пример того же кода (выше) с использованием Redaction:


import { createAction } from 'redaction'

export const initialState = {
 TODO: []
}

export const addTODO = createAction((state, payload) => {
  return { 
    ...state, 
    TODO: [ 
      ...state.TODO, 
      payload 
    ] 
  }
})

Принципиальное отличие: для передачи начального state делается экспорт из файла.


Request Actions


Что касается request экшнов, Redaction предлагает большое кол-во сахара. Пример использования:


Создаем экшн:


// actions/users.js

import { createAction } from 'redaction'

export const getFeed = createAction({
  endpoint: '/api/users/me/posts',
  method: 'GET'
})

Вызываем созданный экшн:


// containers/Users/Feed.js

import actions from 'core/actions'

actions.users.getFeed({
  subset: 'posts'
})

В результате вызова getFeed в state будет:


{
  users: {
    posts: {
      pending: false,
      data: RESPONSE_BODY,
      error: null
    }
  }
}

Стоит заметить что createAction и метод, который он возвращает в результате выполнения принимают одинаковые параметры, т.е. subset можно было бы указать и в createAction, а потом еще его и переопределить при вызове в разных местах.


У каждого subset есть 3 состояния:


1) начало запроса { pending: true, data: null, error: null }
2) запрос выполнен { pending: false, data: RESPONSE_BODY, error: null }
3) запрос выполнен с ошибками { pending: false, data: null, error: RESPONSE_ERROR }


Больше не нужно мучаться с созданием трех экшенов с тремя разными типами для обработки нужного кейса из редьюсера.


Подробнее о createAction


Для отправки запросов внутри используется superagent. createAction в опциях принимает почти все параметры, которые используются в superagent.


Основные опции:


params <Object>


Любой ключ из опций может быть функцией, в этом случае одним из аргументов этой функции будет объект params. Исключениями являются onResponse и onError


endpoint <String | Function>


URL запроса.
Пример использования с params:


export const getFeed = createAction({
  endpoint: ({ userId }) => `/api/users/${userId}/posts`,
  method: 'GET'
})

getFeed({
  params: {
    userId: 100
  }
})

subset <String | Function>


Название ключа, в котором будут храниться данные в state. Стоит учесть, что полный путь будет строиться по схеме: НАЗВАНИЕ_ФАЙЛА.subset


modifyResponse <Function>


Хендлер для редактирования ответа от сервера. Принимает два аргумента — объект Response от сервера и params. Ожидает возвращение нового объекта данных. При этом необходимо возвращать response.body


modifyState <Function>


Хендлер для изменения непосредственно state. Принимает два аргумента — весь объект State и params. Может быть полезно для изменения отдельных частей хранилища при использовании одного экшна. Использовать с осторожностью!


onResponse <Function>


Хендлер, вызывающийся при удачном выполнении запроса, принимает один аргумент — объект Response от сервера


onError <Function>


Хендлер, вызывающийся при невыполнении запроса, принимает два аргумента — объект ошибки и объект Response от сервера


Процесс инициализации Redaction


Я решил не писать на русском процесс инициализации, все это есть в документации на странице репозитория, НО, если будет достаточно желающих — я могу расширить статью, добавив такое описание.


Заключение


Буду рад, если мой модуль окажется полезен. Открыт для вопросов, замечаний и предложений по расширению функциональности. Исходный код модуля, доступен в GitHub репозитории




UPD1: переименовал модуль в RedAction (https://github.com/pavelivanov/redaction)

Теги:
Хабы:
Всего голосов 32: ↑20 и ↓12+8
Комментарии66

Публикации

Истории

Работа

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань