Search code examples
reactjsreact-nativereact-reduxreselect

Component calls Reselect only at the first time it renders


enter image description here

I'm building Multiple Select component for user to select the seasons on the post. So use can choose Spring and Fall. Here I'm using reselect to track selected objects.

My problem is that my reselect doesn't trigger one it renders at the first time. This question looks pretty long but it has many console.log() lines for clarification. So please bear with me! :)

('main.js') Here is my modal Component. Data for this.state.items is seasons = [{id: '100', value: 'All',}, { id: '101', value: 'Spring',}, { ... }]

return (
  <SelectorModal
    isSelectorVisible={this.state.isSelectorVisible}
    multiple={this.state.multiple}
    items={this.state.items}
    hideSelector={this._hideSelector}
    selectAction={this._selectAction}
    seasonSelectAction={this._seasonSelectAction}
  />

('main.js') As you can see I pass _seasonSelectAction to handle selecting items. (It adds/removes an object to/from the array of this.state.selectedSeasonIds). selectedSeasonIds: [] is defined in the state. Let's look at the function.

  _seasonSelectAction = (id) => {
    let newSelectedSeasonIds = [...this.state.selectedSeasonIds, id];
    this.setState({selectedSeasonIds : newSelectedSeasonIds});
    console.log(this.state.selectedSeasonIds); <- FOR DEBUGGING
    console.log(newSelectedSeasonIds); <- For DEBUGGING
  }

I confirmed that it prints ids of selected Item. Probably providing code of SelectorModal.js is irrelevant to this question. So let's move on... :)

('main.js') Here is where I called createSelector

function mapStateToProps(state) {
  return {
    seasons: SelectedSeasonsSelector(state)
  }
}

export default connect(mapStateToProps, null)(...);

(selected_seasons.js) Finally, here is the reselect file

import { createSelector } from 'reselect';

// creates select function to pick off the pieces of states
const seasonsSelector = state => state.seasons
const selectedSeasonsSelector = state => state.selectedSeasonIds

const getSeasons = (seasons, selectedSeasonIds) => {
  const selectedSeasons = _.filter(
    seasons,
    season => _.contains(selectedSeasonIds, season.id)
  );
  console.log('----------------------');
  console.log('getSeasons');
  console.log(selectedSeasons);    <<- You can see this output below
  console.log('seaons');
  console.log(seasons);
  console.log('----------------------');
  return selectedSeasons;
}

export default createSelector(
  seasonsSelector,
  selectedSeasonsSelector,
  getSeasons
);

The output for system console is below

----------------------
getSeasons
Array []
seaons
undefined
----------------------

Thank you for reading this whole question and please let me know if you have any question on this problem.

UPDATE As Vlad recommended, I put SelectedSeasonsSelector inside of _renderSeasons but it prints empty result like above every time I select something. I think it can't get state.seasons, state.

  _renderSeasons = () => {
      console.log(this.state.seasons) // This prints as expected. But this becomes undefined 
                                      //when I pass this.state in to the SelectedSeasonsSelector
      selectedSeasons = SelectedSeasonsSelector(this.state)
      console.log('how work');
      console.log(selectedSeasons);
      let seasonList = selectedSeasons.map((season) => {
          return ' '+season;
      })
      return seasonList
  }

state.seasons and state.selectedSeasonsIds are getting undefined


Solution

  • Looks like you are assuming that this.setState will change redux store, but it won't.

    In a _seasonSelectAction method you are calling this.setState that stores selected ids in container's local state.

    However selectors are expect ids will be be stored in global redux store.

    So you have to pass selected id's to redux store, instead of storing them in a local state. And this parts are looks missing:

    1. dispatch action
    2. use reducer to store this info into redux store.
    3. add mapDispatchToProps handler to your connect

    I'm guessing here, but it looks like confusing terms:component local state is not the same as redux store state. First one is local to your component and can be accessed through this.state attributive. Second is global data related to all of your application, stored in redux store and could be accessed by getState redux method.

    I so you probably have to decide, whether to stay with redux stack or create pure react component. If pure react is your choice, than you dint have to use selectors, otherwise you have to dispatch action and more likely remove this.state.