У меня есть сценарий, в котором у меня есть 2 редуктора, которые являются результатом combineReducers
. Я хочу объединить их вместе, но держу их ключи на одном уровне при вложении.
Например, учитывая следующие редукторы
const reducerA = combineReducers({ reducerA1, reducerA2 })
const reducerB = combineReducers{{ reducerB1, reducerB2 })
Я хочу закончить со структурой как:
{
reducerA1: ...,
reducerA2: ...,
reducerB1: ...,
reducerB2: ...
}
Если я снова использую combineReducers
с reducerA
на reducerA
и reducerB
вот так:
const reducer = combineReducers({ reducerA, reducersB })
Я в конечном итоге со структурой, как:
{
reducerA: {
reducerA1: ...,
reducerA2: ...
},
reducerB: {
reducerB1: ...,
reducerB2: ...
}
}
Я не могу совмещать reducerA1
, reducerA2
, reducerB1
и reducerB2
в едином combineReducers
называют reducerA
и reducerB
которые были предоставлено мне уже сложены из различных пакетов НПХ.
Я попытался использовать библиотеку Reducer-Redurs, чтобы объединить их вместе и уменьшить состояние вместе, идея, которую я получил, глядя на документы DX, вот так:
const reducer = reduceReducers(reducerA, reducerB)
К сожалению, это не сработало, так как результирующий редуктор от combineReducers
предупреждение, если неизвестные ключи найдены, и игнорирует их при возврате своего состояния, поэтому результирующая структура содержит только структуру reducerB
:
{
reducerB1: ...,
reducerB2: ...
}
На самом деле я не хочу реализовывать свой собственный combineReducers
, который не строго combineReducers
за структурой, если мне это не нужно, поэтому я надеюсь, что кто-то знает о другом способе, либо встроенном в redux, либо из библиотеки, которая может помоги мне с этим. Есть идеи?
Редактировать:
Был получен ответ (кажется, что он был удален), в котором предлагалось использовать библиотеку flat-Объединение-редукторы:
const reducer = flatCombineReducers(reducerA, reducerB)
Это было на один шаг ближе к редукторам в том reducerB
, что ему удавалось сохранять состояние сохранения как для reducerA
и для reducerB
, но все еще создаются предупреждающие сообщения, что заставляет меня задаться вопросом, не было ли в состоянии исчезновения, которое я наблюдал ранее, не combineReducers
далеко, а скорее что-то еще происходит с реализацией редукторов.
Предупреждающие сообщения:
Неожиданные ключи "reducerB1", "reducerB2", найденные в предыдущем состоянии, получены редуктором. Ожидается, что вместо этого будет найден один из известных ключей редуктора: "reducerA1", "reducerA2". Неожиданные ключи будут игнорироваться.
Неожиданные ключи "reducerA1", "reducerA2", найденные в предыдущем состоянии, получены редуктором. Ожидается, что вместо этого будет найден один из известных ключей редуктора: "reducerB1", "reducerB2". Неожиданные ключи будут игнорироваться.
Если я сделаю производственную сборку, предупреждение исчезнет (таков путь для многих предупреждений о реакции/избыточности), но я бы предпочел, чтобы они вообще не появлялись.
Я также сделал еще несколько поисков для других библиотек и нашел redux-concatenate-redurs:
const reducer = concatenateReducers([reducerA, reducerB])
Это имеет тот же результат, что и плоские комбайны-редукторы, поэтому поиск продолжается.
Изменить 2:
Несколько человек сделали некоторые предложения сейчас, но ни один не работал до сих пор, так что вот тест, чтобы помочь:
import { combineReducers, createStore } from 'redux'
describe('Sample Tests', () => {
const reducerA1 = (state = 0) => state
const reducerA2 = (state = { test: "value1"}) => state
const reducerB1 = (state = [ "value" ]) => state
const reducerB2 = (state = { test: "value2"}) => state
const reducerA = combineReducers({ reducerA1, reducerA2 })
const reducerB = combineReducers({ reducerB1, reducerB2 })
const mergeReducers = (...reducers) => (state, action) => {
return /* your attempt goes here */
}
it('should merge reducers', () => {
const reducer = mergeReducers(reducerA, reducerB)
const store = createStore(reducer)
const state = store.getState()
const expectedState = {
reducerA1: 0,
reducerA2: {
test: "value1"
},
reducerB1: [ "value" ],
reducerB2: {
test: "value2"
}
}
expect(state).to.deep.equal(expectedState)
})
})
Цель состоит в том, чтобы этот тест прошел и не выдавал никаких предупреждений в консоли.
Изменить 3:
Добавлено больше тестов, чтобы охватить больше случаев, включая обработку действия после первоначального создания и создание хранилища с начальным состоянием.
import { combineReducers, createStore } from 'redux'
describe('Sample Tests', () => {
const reducerA1 = (state = 0) => state
const reducerA2 = (state = { test: "valueA" }) => state
const reducerB1 = (state = [ "value" ]) => state
const reducerB2 = (state = {}, action) => action.type == 'ADD_STATE' ? { ...state, test: (state.test || "value") + "B" } : state
const reducerA = combineReducers({ reducerA1, reducerA2 })
const reducerB = combineReducers({ reducerB1, reducerB2 })
// from Javaguru answer
const mergeReducers = (reducer1, reducer2) => (state, action) => ({
...state,
...reducer1(state, action),
...reducer2(state, action)
})
it('should merge combined reducers', () => {
const reducer = mergeReducers(reducerA, reducerB)
const store = createStore(reducer)
const state = store.getState()
const expectedState = {
reducerA1: 0,
reducerA2: {
test: "valueA"
},
reducerB1: [ "value" ],
reducerB2: {}
}
expect(state).to.deep.equal(expectedState)
})
it('should merge basic reducers', () => {
const reducer = mergeReducers(reducerA2, reducerB2)
const store = createStore(reducer)
const state = store.getState()
const expectedState = {
test: "valueA"
}
expect(state).to.deep.equal(expectedState)
})
it('should merge combined reducers and handle actions', () => {
const reducer = mergeReducers(reducerA, reducerB)
const store = createStore(reducer)
store.dispatch({ type: "ADD_STATE" })
const state = store.getState()
const expectedState = {
reducerA1: 0,
reducerA2: {
test: "valueA"
},
reducerB1: [ "value" ],
reducerB2: {
test: "valueB"
}
}
expect(state).to.deep.equal(expectedState)
})
it('should merge basic reducers and handle actions', () => {
const reducer = mergeReducers(reducerA2, reducerB2)
const store = createStore(reducer)
store.dispatch({ type: "ADD_STATE" })
const state = store.getState()
const expectedState = {
test: "valueAB"
}
expect(state).to.deep.equal(expectedState)
})
it('should merge combined reducers with initial state', () => {
const reducer = mergeReducers(reducerA, reducerB)
const store = createStore(reducer, { reducerA1: 1, reducerB1: [ "other" ] })
const state = store.getState()
const expectedState = {
reducerA1: 1,
reducerA2: {
test: "valueA"
},
reducerB1: [ "other" ],
reducerB2: {}
}
expect(state).to.deep.equal(expectedState)
})
it('should merge basic reducers with initial state', () => {
const reducer = mergeReducers(reducerA2, reducerB2)
const store = createStore(reducer, { test: "valueC" })
const state = store.getState()
const expectedState = {
test: "valueC"
}
expect(state).to.deep.equal(expectedState)
})
it('should merge combined reducers with initial state and handle actions', () => {
const reducer = mergeReducers(reducerA, reducerB)
const store = createStore(reducer, { reducerA1: 1, reducerB1: [ "other" ] })
store.dispatch({ type: "ADD_STATE" })
const state = store.getState()
const expectedState = {
reducerA1: 1,
reducerA2: {
test: "valueA"
},
reducerB1: [ "other" ],
reducerB2: {
test: "valueB"
}
}
expect(state).to.deep.equal(expectedState)
})
it('should merge basic reducers with initial state and handle actions', () => {
const reducer = mergeReducers(reducerA2, reducerB2)
const store = createStore(reducer, { test: "valueC" })
store.dispatch({ type: "ADD_STATE" })
const state = store.getState()
const expectedState = {
test: "valueCB"
}
expect(state).to.deep.equal(expectedState)
})
})
Приведенная выше реализация mergeReducers
проходит все тесты, но по-прежнему mergeReducers
предупреждения консоли.
Sample Tests
✓ should merge combined reducers
✓ should merge basic reducers
Unexpected keys "reducerB1", "reducerB2" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "reducerA1", "reducerA2". Unexpected keys will be ignored.
Unexpected keys "reducerA1", "reducerA2" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "reducerB1", "reducerB2". Unexpected keys will be ignored.
✓ should merge combined reducers and handle actions
✓ should merge basic reducers and handle actions
✓ should merge combined reducers with initial state
✓ should merge basic reducers with initial state
✓ should merge combined reducers with initial state and handle actions
✓ should merge basic reducers with initial state and handle actions
Важно отметить, что печатаемые предупреждения относятся к тестовому случаю сразу же после этого, и что редукторы combineReducers
будут печатать каждое уникальное предупреждение только один раз, поэтому, поскольку я повторно использую редуктор между тестами, предупреждения отображаются только для первого тестового случая. чтобы произвести это (я мог бы объединить редукторы в каждом тесте, чтобы предотвратить это, но поскольку критерии, которые я ищу, чтобы вообще не производить их, я доволен этим пока).
Если вы пытаетесь это сделать, я не возражаю, если mergeReducers
принимает 2 редуктора (как выше), массив редукторов или объект редукторов (например, combineReducers
). На самом деле, я не возражаю против того, как это достигается, если не требуется никаких изменений в создании reducerA
reducerB
, reducerA1
reducerA1
, reducerB1
reducerA
, reducerB
reducerA1
, reducerA1
reducerB1
или reducerB2
.
Изменить 4:
Мое текущее решение модифицировано из ответа Jason Geomaat.
Идея состоит в том, чтобы отфильтровать состояние, предоставляемое редуктору, используя ключи предыдущих вызовов, используя следующую оболочку:
export const filteredReducer = (reducer) => {
let knownKeys = Object.keys(reducer(undefined, { type: '@@FILTER/INIT' }))
return (state, action) => {
let filteredState = state
if (knownKeys.length && state !== undefined) {
filteredState = knownKeys.reduce((current, key) => {
current[key] = state[key];
return current
}, {})
}
let newState = reducer(filteredState, action)
let nextState = state
if (newState !== filteredState) {
knownKeys = Object.keys(newState)
nextState = {
...state,
...newState
}
}
return nextState;
};
}
Я объединяю результаты отфильтрованных редукторов с использованием библиотеки redux-concatenate-redurs (возможно, использовались flat-Объединить-редукторы, но реализация первого слияния кажется немного более надежной). Функция mergeReducers
выглядит следующим образом:
const mergeReducers = (...reducers) => concatenateReducers(reducers.map((reducer) => filterReducer(reducer))
Это называется так:
const store = createStore(mergeReducers(reducerA, reducerB)
Это проходит все тесты и не combineReducers
никаких предупреждений от редукторов, созданных с помощью combineReducers
.
Единственный бит, в котором я не уверен, - это где массив knownKeys
вызовом редуктора с действием INIT
. Это работает, но кажется немного грязным. Если я этого не сделаю, то выдается только предупреждение, если хранилище создано с начальным состоянием (дополнительные ключи не отфильтровываются при разрешении начального состояния редуктора).