I'm developing a large application with Redux and React and I decided to make some reusable components like custom checkboxes and radio buttons.
As I want to store this information into the Redux store to be able to debug state of the application easily and the same component have the same functionality everywhere, it seems a good idea to create reducers and actions for each component.
However Redux reducers return a new state and save it in the store with the name of the reducer as a key and this forbids having more than one component of the same type in the same page using the same actions and reducers but keeping different states.
I think there are two solutions to this problem:
Create different actions and reducers for each component that I use, even thought the component and it's functionalities are the same. This solutions doesn't seem a good solution because there will be a lot of redundant code.
Create actions with sufficient parameters to be able to differentiate each one in the reducer and that way change only the part of the state that is specified.
I went forward with the second option.
Actions file for the CheckBox component:
import {createAction} from 'redux-actions';
/****** Actions ******/
export const CHANGE_CHECKBOX_STATE = "CHANGE_CHECKBOX_STATE";
/****** Action creators ******/
export const changeCheckboxState = createAction(CHANGE_CHECKBOX_STATE, (block, name, state) => {
return {
block,
name,
state: {
checked: state,
}
};
});
Reducers file for the CheckBox component:
import {handleActions} from 'redux-actions';
import {CHANGE_CHECKBOX_STATE} from './CheckBox.actions';
export const checkBoxComponent = handleActions({
CHANGE_CHECKBOX_STATE: (state, action) => ({
[action.payload.block]: {
[action.payload.name]: action.payload.state
}
})
}, {});
I use block
to specify the page, name
to specify the name of the specific component (e.g. gender) and state as and object with the new state.
But this solution has some problems too:
- Can't specify initial state of each component because the keys of the state are dynamic.
- Complicates to much the store structure with a lot of nested states.
- Aggregates data by component and not by form which will complicate debugging and it's logically incorrect.
I don't have enough experience with Redux and React to think of a better solution to this problem. But it seems to me that I'm missing something important about React-Redux relationship and this raises some questions:
Is it a good idea to store state of this reusable components into the Redux store?
Am I wrong in binding together React components with Redux actions and reducers?
I'm developing a large application with Redux and React and I decided to make some reusable components like custom checkboxes and radio buttons.
As I want to store this information into the Redux store to be able to debug state of the application easily and the same component have the same functionality everywhere, it seems a good idea to create reducers and actions for each component.
However Redux reducers return a new state and save it in the store with the name of the reducer as a key and this forbids having more than one component of the same type in the same page using the same actions and reducers but keeping different states.
I think there are two solutions to this problem:
Create different actions and reducers for each component that I use, even thought the component and it's functionalities are the same. This solutions doesn't seem a good solution because there will be a lot of redundant code.
Create actions with sufficient parameters to be able to differentiate each one in the reducer and that way change only the part of the state that is specified.
I went forward with the second option.
Actions file for the CheckBox component:
import {createAction} from 'redux-actions';
/****** Actions ******/
export const CHANGE_CHECKBOX_STATE = "CHANGE_CHECKBOX_STATE";
/****** Action creators ******/
export const changeCheckboxState = createAction(CHANGE_CHECKBOX_STATE, (block, name, state) => {
return {
block,
name,
state: {
checked: state,
}
};
});
Reducers file for the CheckBox component:
import {handleActions} from 'redux-actions';
import {CHANGE_CHECKBOX_STATE} from './CheckBox.actions';
export const checkBoxComponent = handleActions({
CHANGE_CHECKBOX_STATE: (state, action) => ({
[action.payload.block]: {
[action.payload.name]: action.payload.state
}
})
}, {});
I use block
to specify the page, name
to specify the name of the specific component (e.g. gender) and state as and object with the new state.
But this solution has some problems too:
- Can't specify initial state of each component because the keys of the state are dynamic.
- Complicates to much the store structure with a lot of nested states.
- Aggregates data by component and not by form which will complicate debugging and it's logically incorrect.
I don't have enough experience with Redux and React to think of a better solution to this problem. But it seems to me that I'm missing something important about React-Redux relationship and this raises some questions:
Is it a good idea to store state of this reusable components into the Redux store?
Am I wrong in binding together React components with Redux actions and reducers?
- I don't understand why you'd need to store a control's state like this? Isn't backed by actual data? And that data doesn't give no craps about how it's rendered? Seems like bloat to me. – Chris Martin Commented Sep 7, 2015 at 10:54
- @ChrisMartin I tried to explain here how I arrived to this decision, but I also said that I know i'm doing something wrong, I just don't see what it is. Can you explain to me how you would design it? Maybe that way I will see what's wrong with my approach. Thanks for responding! – Roc Commented Sep 8, 2015 at 14:58
- Please, watch these videos: https://egghead.io/series/getting-started-with-redux... – Mike Commented Feb 4, 2016 at 18:16
2 Answers
Reset to default 11Don't describe your Redux reducers (your overall application state) in terms of the components that consume that state. Track it the other way, describing your application state in a descriptive manner and then have your "dumb" components consume that descriptive state.
Rather than tracking "The state of this checkbox for the form related to foo", track "this boolean value of foo" and then have the checkbox consume that state.
An example store for the checkbox:
const initialState = {
someFooItem: { isCertainType: false }
};
export function foos(state = initialState, action) {
switch(action.type){
case(UPDATE_FOO_VALUE):
return {
...state,
[action.payload.id]: {
isCertainType: action.payload.isCertainType
}
}
}
}
An example checkbox consuming the store
class CheckBox extends React.Component {
render() {
return <input type="checkbox" checked={this.props.checked} />
}
}
The parent component
class ParentComponent extends React.Component {
render() {
return <CheckBox checked={this.foo.isCertainType} />
}
}
The fundamental mistake here is that you have 0 dumb components. Basic controls like checkboxes should be dumb components using just props, their parent having the responsibility to select and update the proper piece of state for a particular checkbox.