Reputation: 2853
I have two reducers, the first one fetches data and the second one is used to filter the data based on changes to the original data. I have read that using combineReducers
is not an option.
My postReducer
import {FETCH_POST} from '../actions/types';
const initialState = {
items: [],
item: {}
};
export default function(state = initialState, action){
switch (action.type){
case FETCH_POST:
return Object.assign({}, state, {items: action.payload});
default:
return state;
}
}
My pinReducer
import {PIN_POST} from '../actions/types';
const initialState = {
items: [],
item: {}
};
export default function(state = initialState, action){
switch (action.type){
case PIN_POST:
console.log(state.items);
const item = state.items.map(value => value.data.id === action.id ?
{data: Object.assign({}, value.data, {pinned: action.val})} : value
);
//console.log(item);
return Object.assign({}, state, {items: item });
default:
return state;
}
}
Main
import {combineReducers} from 'redux';
import postReducer from './postReducer';
import pinReducer from './pinReducer';
export default combineReducers({
post: postReducer,
pin: pinReducer
});
How can I share the state between the two reducers, as state.items
in the pinReducer is empty
Upvotes: 4
Views: 5978
Reputation: 5081
Reducer does not access to store's state, nor other reducers' previous state. Only to its own previous state and dispatched action. You can think of it as a single brick in the wall of redux' state.
In your case, we have to tackle the logic and decide what is an actual state here.
Requirements:
Solution:
1 - initial posts reducer:
const initialState = { items: [] }
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_POSTS:
return { items: action.payload }
default:
return state
}
}
2 - add info about pinned post:
const initialState = { items: [], pinnedId: null }
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_POSTS:
return { ...state, items: action.payload.posts }
case PIN_POST:
return { ...state, pinnedId: action.payload.id }
default:
return state
}
}
3 - get a currently pinned post:
// assuming that store is configured...
export default combineReducers({ posts: postsReducer })
// ...
// somewhere in `connect`
const mapStateToProps = (state, ownProps) => {
return {
pinnedPost: state.posts.find(post => post.id === state.posts.pinnedId),
}
}
So now we store a minimum information about a pinned post (only its id) and it is enough to get an actual pinned post.
Also, as mentioned in another answer by @shubham-khatri, you can use optimized selectors. With current shape of the state storing pinned id separately, it is even more efficient since your selector will depend on two inputs: posts array (is re-created on fetch, so shallow comparable) and pinned id (is a primitive number or string, also easy to compare).
I'd encourage you to store as minimum information in store as possible. Also, store it in simple and flat shape. Move everything that's can be calculated to selectors.
Upvotes: 1
Reputation: 282160
You should not have a reducers that has state derived on the basis of other reducers. What you need in your case is a selector(more efficiently a memoizedSelector), for which you can use reselect
library
My postReducer
import {FETCH_POST} from '../actions/types';
const initialState = {
items: [],
item: {}
};
export default function(state = initialState, action){
switch (action.type){
case FETCH_POST:
return Object.assign({}, state, {items: action.payload});
default:
return state;
}
}
Main
import {combineReducers} from 'redux';
import postReducer from './postReducer';
export default combineReducers({
post: postReducer,
});
and then where you want to use pinItem, you can do that in mapStateToProps
getPinItem = (state, props) => {
const item = state.items.map(value => value.data.id === action.id ?
{data: Object.assign({}, value.data, {pinned: action.val})} : value
);
return item;
}
const mapStateToProps = (state, props) => {
const pinItem = getPinItem(state, props);
return {
pinItem
}
}
Upvotes: 1