Комментарии 18
А чем вас не устраивает стандартный способ упорядочиваения методов с import/export?
Можно положить экшены и редюсер в один файл, такой подход называется Ducks.
export const actionA () => {/*action code*/}
export const actionB () => {/*action code*/}
export default function reducer(state, action) {
// reducer code
}
использование:
// если нужен редьюсер
import reducer from './ducks';
// если нужны экшены
import {actionA, actionB} from './ducks';
Можно завернуть символы в фабрику:
function createList() {
const ACTION_A = Symbol();
const ACTION_В = Symbol();
return {
actionA: (payload) => ({type: ACTION_A, payload}),
actionB: (payload) => ({type: ACTION_B, payload}),
reducer: (state, action) => {
// reducer code
}
};
}
Большой разницы между createList()
и new List()
нет, зато не нужно делать никаких оговорок типа "Генераторы экшенов и редьюсер должны быть методами экземпляра класса", как у вас в статье.
Стараемся сделать так, чтобы reducer's можно было экспортировать / импортировать
как обычную «библиотеку».
### ------------------------------------------------ actions.coffee ###
{ createAction } = require 'redux-actions'
###
DATA API
###
module.exports =
# PUBLIC
create: createAction 'CREATE_MESSAGE', (body, header) ->
action =
body: body
header: header
action
remove: createAction 'REMOVED_MESSAGE', (id) ->
id: id
# Private
$:
creating: createAction 'CREATING_MESSAGE', (message) ->
_.assign {}, message,
id: _.uniqueId('message-')
synced: false
created: createAction 'CREATED_MESSAGE'
### ------------------------------------------------ reducer.coffee ###
_ = require 'lodash'
{ handleActions } = require 'redux-actions'
###
STATE CHANGES
###
actions = require './actions'
actionsMap = [
action: actions.$.creating
handle: (state, action) ->
_.concat state, _.assign {}, action.payload
,
action: actions.$.created
handle: (state, action) ->
_.map _.cloneDeep(state), (item) ->
if item.id is action.payload
item.synced = true
item
,
action: actions.remove
handle: (state, action) ->
_.reject _.cloneDeep(state), action.payload
]
module.exports =
messages: handleActions _.mapValues(_.keyBy(actionsMap, 'action'), 'handle'), []
/* ------------------------------------------------ actions.coffee */
var _, actions, actionsMap, createAction, handleActions;
createAction = require('redux-actions').createAction;
/*
DATA API
*/
module.exports = {
create: createAction('CREATE_MESSAGE', function(body, header) {
var action;
action = {
body: body,
header: header
};
return action;
}),
remove: createAction('REMOVED_MESSAGE', function(id) {
return {
id: id
};
}),
$: {
creating: createAction('CREATING_MESSAGE', function(message) {
return _.assign({}, message, {
id: _.uniqueId('message-'),
synced: false
});
}),
created: createAction('CREATED_MESSAGE')
}
};
/* ------------------------------------------------ reducer.coffee */
_ = require('lodash');
handleActions = require('redux-actions').handleActions;
/*
STATE CHANGES
*/
actions = require('./actions');
actionsMap = [
{
action: actions.$.creating,
handle: function(state, action) {
return _.concat(state, _.assign({}, action.payload));
}
}, {
action: actions.$.created,
handle: function(state, action) {
return _.map(_.cloneDeep(state), function(item) {
if (item.id === action.payload) {
item.synced = true;
}
return item;
});
}
}, {
action: actions.remove,
handle: function(state, action) {
return _.reject(_.cloneDeep(state), action.payload);
}
}
];
module.exports = {
messages: handleActions(_.mapValues(_.keyBy(actionsMap, 'action'), 'handle'), [])
};
Прочитал с листа код из примера, а из статьи смог вынести полезного про уникальность возвращаемых значений символов.
Поставьте себя на место тех, кто будет на поддержке кода. Пожалуйста!
Ох, вообще печально это всё.
const INCREMENT = "counter1/INCREMENT";
const DECREMENT= "counter1/DECREMENT";
...
function counter(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
}
Так мы еще сможем сохранить информацию для дев-тулз, где четко будет видно какой именно экшн сработал.
Красивый способ избавиться от switch:
import { createAction } from 'redux-act'
export const inputTitle = createAction('@@edit_post/INPUT_TITLE', title => ({ title }))
export const submit = createAction('@@edit_post/SUBMIT', post => post)
import { createReducer } from 'redux-act'
import * as EditPostActions from '../actions/EditPostActions'
const initialState = {
flow: '',
title: '',
content: ''
}
const reducer = createReducer({
[EditPostActions.inputTitle]: (state, { title }) => ({...state, title}),
[EditPostActions.submit]: (state, post) => ({...state, ...post})
}, initialState)
export default reducer
Спасибо за ducks, теперь вообще замечательно!
import { createAction, createReducer } from 'redux-act'
export const actions = {
inputTitle: createAction('@@edit_post/INPUT_TITLE', title => ({ title })),
submit: createAction('@@edit_post/SUBMIT', post => post),
}
const initialState = {
flow: '',
title: '',
content: ''
}
const reducer = createReducer({
[actions.inputTitle]: (state, { title }) => ({...state, title}),
[actions.submit]: (state, post) => ({...state, ...post})
}, initialState)
export default reducer
List.devtools.ready.js
Я для решения проблемы дублирования редьюсеров использовал один редьюсер, который хранит в себе несколько стейтов, которые могут динамически создаваться и обращаться к ним можно по имени
const COUNTER_INCREMENT = "counter increment"
const COUNTER_DECREMENT = "counter decrement"
const COUNTER_INIT = "counter init"
const getInitialCounter = ({
counter: 0
})
const counter(initialState = {}, action) {
const {payload, type} = action;
switch(type) {
case COUNTER_INIT: {
const {name} = payload
return {
...state,
[name]: getInitialCounter()
}
},
case COUNTER_INCREMENT: {
const {name} = payload
const oldCounter = state[name]
const updatedCounter = {
counter: oldCounter.counter++
}
return {
...state,
[name]: updatedCounter
}
}
case COUNTER_DECREMENT: {
const {name} = payload
const oldCounter = state[name]
const updatedCounter = {
counter: oldCounter.counter--
}
return {
...state,
[name]: updatedCounter
}
}
default return state;
}
}
const incrementCounter(name) => ({type: COUNTER_INCREMENT, name})
const decrementCounter(name) => ({type: COUNTER_DECREMENT, name})
const initCounter = (name) => ({type: COUNTER_INIT, name})
C девтулзом хорошо дружит, потому что всегда можно посмотреть, какое имя передается в payload
и тем самым понять, какой именно стейт в редьюсере будет изменен
Современный JavaScript или как сделать ваш Redux-модуль готовым к переиспользованию