Search code examples
reduxreact-reduxredux-thunk

Redux pattern for populating the state, and then building components from that state?


I've got a component that builds search/sort filters that can be selected. I want the selected state of those filters to be tracked in redux so that the search builder can subscribe and see when they change and update appropriately. the thing I'm trying to figure out how to do (in a way that doesn't feel weird) is populate the filter objects into the state. Eg, right now in the <Search /> component I have something like:

<OptionPicker
  group={'searchFilters'}
  options={{word: 'price', active: true},
  {word: 'distance', active: false},
  {word: 'clowns', active: false}}
/>

So how to get those props into state to be used without triggering multiple element renders. I'm also rendering the app on the server as well, so for the initial attachment render, the state already has the options.

In the OptionPicker component I've got:

class OptionPicker extends Component {
  constructor(props) {
    super(props)
    if (!props.optionstate) {
      this.props.addOptionState(props)
    }
  }

  render() {
    return {this.props.optionstate.word.map((word) => <Option ... />)}
  }
}


function mapStateToProps(state, props) {
  return {
    optionstate: state.optionstate[props.group],
  };
}

function mapDispatchToProps(dispatch) {
  return {
    addOptionState: (props) => {
      dispatch(addOptionState(props));
    },
    optionToggled: (group, word) => {
      dispatch(updateOptionState(group, word));
    }
  };
}

export default connect(mapStateToProps,mapDispatchToProps)(OptionGroup);

This kinda works, but there exists a time when render is called before the redux state has been populated, which throws an error. I could guard against that, but none of this feels "right". Shouldn't that prop always be there? Is there a pattern for this that I'm missing?


Solution

  • I agree with you in that the prop should always be there. The pattern I use for this is to set up an initial state and to pass it to the reducer function:

    export const INITIAL_STATE = {
      optionstate: { /* all filters are present but deactivated */ }
    };
    
    export default function (state = INITIAL_STATE, action) {
      // reduce new actions into the state
    };