spondbob spondbob - 2 months ago 38
React JSX Question

reuse redux reducers and actions

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;

Answer

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,
});