Reputation: 1633
Lets say I have a section reducer to hold the section's data
const initialState = {
data: [],
section: ''
};
const sectionReducer = function(state = initialState, action) {
switch(action.type) {
case types.IMPORT_DATA_SUCCESS:
return Object.assign(
{},
state,
{
section: action.section,
data: action.data
}
);
}
return state;
}
And I need couple sections with the same data structure and actions. Then how can I do it like this
import { combineReducers } from 'redux';
import sectionReducer from './sectionReducer';
const reducers = combineReducers({
sectionA: sectionReducer,
sectionB: sectionReducer
});
export default reducers;
The action is as simple as
import sectionAData from '../data/sectionAData';
...
export const importDataSuccess = (section, data) => {
return {
type: types.IMPORT_DATA_SUCCESS,
section: section,
data
}
}
export const loadData = (section) => {
const dataSet = (() => {
switch(section) {
case "SECTIONA":
return sectionAData
break;
case "SECTIONB":
return sectionBData
break;
}
})()
return (dispatch) => {
dispatch(importDataSuccess(section, dataSet))
}
}
The problem with above approach is everytime I call this.props.dispatch(loadData("SECTIONA"))
on componentA
and this.props.dispatch(loadData("SECTIONB"))
on componentB
I get sectionBData
on both state tree sectionA
and sectionB
. Even when sectionA
is first dispatched, the sectionB
already populated with sectionA
data even though it has not been dispatched.
What is the proper way to reuse the reducer and action for such case? Or do I have to create action and reducer for each section even though they are the same?
======= UPDATE =====
Reducer
const initialState = {
data: []
};
const sectionReducer = function(state = initialState, action) {
switch(action.type) {
case types.IMPORT_DATA:
if ( action.section ) {
return state.data.push({ section: action.section, data: action.data });
}
break;
}
return state;
}
...
const reducers = combineReducers({
sections: sectionReducer
});
export default reducers;
Action
export const importData= (section, data) => {
return {
type: types.IMPORT_DATA,
section,
data
}
}
export const loadData = (section) => {
const dataSet = (() => {
switch(section) {
case "SECTIONA":
return sectionAData
break;
case "SECTIONB":
return sectionBData
break;
}
})()
return (dispatch) => {
dispatch(importData(section, dataSet))
}
}
The component
class SectionWrapper extends Component {
componentDidMount() {
this.props.dispatch(loadData(this.props.const))
}
render() {
return (
<div>
<Section
title={this.props.title}
id={this.props.id}
data={this.props.data} />
</div>
)
}
}
const mapStateToProps = function(store, ownProps) {
const section = store.sections.find( (item) => { return item.section === ownProps.const; })
const data = section.data
return {
data
};
}
SectionWrapper.propTypes = {
title: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
const: PropTypes.string.isRequired
}
export default connect(mapStateToProps)(SectionWrapper);
Store
const createStoreWithMiddleware = compose(
applyMiddleware(
thunkMiddleware,
createLogger()
),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
const store = createStore(
reducers,
createStoreWithMiddleware
);
export default store;
Upvotes: 0
Views: 1006
Reputation: 6571
Right now, when sectionReducer
receives an action of type IMPORT_DATA_SUCCESS
, It replaces the entire state with the data received... so there could be only one section and data in the state., Which is obviously what you don't want.
What you can do is, In your sectionReducer
const initialState = {
data: [],
};
const sectionReducer = function(state = initialState, action) {
switch(action.type) {
case types.IMPORT_DATA_SUCCESS:
// action contains { type, section, data }
// added if statement to make sure data is not empty.
if ( action.section && action.data ) {
state.data.push({ section: action.section, data: action.data });
return Object.assign( {}, state );
}
return state;
case types.UPDATE_IMPORTED_DATA:
// action contains { section, data }
let index = state.data.findIndex( item => { return item.section === action.section; });
state.data[index] = { section: action.section, data: action.data }
return state;
default:
return state;
}
}
Using this method you can store n
number of sections and also update them very easily.
And when you want to access the data of specific section, you can do
let findSectionByName = ( sections, section_name ) => {
return sections.find( (item) => { return item.section === section_name; })
}
let section = findSectionByName( state.sections, 'SECTIONA' );
You will also have to change
const reducers = combineReducers({
sectionA: sectionReducer,
sectionB: sectionReducer
});
to
const reducers = combineReducers({
sections: sectionReducer,
});
Upvotes: 2