Redux and Reselect: Using derived data to compute

2019-09-08 16:51发布

问题:

What is the best approach to use one part of the redux state tree to update another part of the state. In particular, I have a simplified flashcard app example below. We have a list of words maintained by one reducer, and then another part of the state tree is maintained by a quiz reducer, which holds the currently chosen word. I would like the quiz reducer to have access to the list of words, and maybe even a computed (memoized) filtered subset of the words. What is the best approach here?

// The reducer that manages a list of words
function wordsReducer(state = [], action) {
  switch(action.type) {
  ...
  default:
    return state;
  }
};

// text to filter words with
function filterReducer(state = '', action) {
  switch(action.type) {
  case SET_FILTER:
    return action.payload;
  default:
    return state:
  }
};

// we have derived data, which is a list of filtered words
const filteredWordsSelector = createSelector(
  (state) => state.words,
  (state) => state.filter,
  (words, filter) => words.filter(word => word.indexOf(filter) >= 0)
);

//return one word randomly.
function chooseWord(words) {
  return words[Math.floor(Math.random()*words.length)];
}

Here is my confusion below. How can I access computed data inside another branch of the state. Also, I want to add complexity to this, like sorting the words into buckets, which is why I thought reselect (or some other memoization) would be a good approach.

The following answer suggests that I should not use reselect inside my reducers, but is there another approach to my problem? https://stackoverflow.com/a/32922461/2620595

const initialQuizState = {chosenWord: null};

function quizReducer(state = initialQuizState, action, filteredWords) {
  switch(action.type) {
  case CHOOSE_WORD:
    // I want to choose a word at random from filteredWords
    return {...state, chosenWord: chooseWord(filteredWords)};
  default:
    return state;
}

function rootReducer(state, action) {
  return {
    words: wordsReducer(state.words, action),
    filter: filterReducer(state.filter, action),
    quiz: quizReducer(state.quiz, action, filteredWordsSelector(state))
  }
};

What is a good approach to organizing my redux reducers? Should I be passing another part of the state into the quizReducer above and is it really bad to use reselect in this case?

回答1:

I don't think using selectWord in your reducer is a violation of Dan Abramov's advice, since it's not actually a pure selector. The use of Math.random() means you're deriving your result from something other than state.

A more purist approach might be to simply store the result of Math.random() in the state, and derive everything else. Though I hesitate to advocate this, since it has the potential downside of making your code more difficult to understand.