javascript - Redux: Tyngre logik i Action Creators saktar ner UI? (Borde det omslutas med setTimeout()?)


Ska dyr logik i Redux Toolkit Action Creators ligga i setTimeout()?

Användningsfallet är en filtreringsruta som filtrerar data. I koden nedan finns en åtgärdsskapare som är ansvarig för att först uppdatera tillståndet för filtreringsrutan och sedan uppdatera de filtrerade uppgifterna.

När jag inte kapslar in filtreringslogiken i setTimeout finns det en märkbar fördröjning i användargränssnittet för filtreringsrutan (att ändra filtreringsorden försvåras). Är det alltså en rekommenderad praxis att lägga tyngre logik inom action creators i något som setTimeout()?

export const filteringWordsChanged = (newWords: string) => (dispatch: any, getState: any) => {

    // Send Action to make sure the filter box insides are updated. 
    dispatch(updateFilteringWords(newWords));

    let callback = () => {
        let filtered = performFilteringLogic(getState(), newWords);
        dispatch(updateFilteredResults(filtered));
    };

    // callback();
    setTimeout(callback, 0);
}

Svar #1

Så jag antar att performFilteringLogic inte är async, det är bara långsamt? Min instinkt säger att detta bäst hanteras inne i komponenten med de två åtgärderna som dispatchas separat. Vi vill att ändringarna i words ska vara omedelbara, medan uppdateringen av de filtrerade resultaten kan ta lite tid (som en bisats: filtrering är vanligtvis den typ av sak som görs genom en memoiserad selector snarare än genom att lagra de filtrerade resultaten i din store).

Här anropar callbacken på input onChange-händelsen omedelbart updateFilteringWords. Det kommer att göra att variabeln words från selectorn ändras. Effekten lyssnar på förändringar i words. Om words inte är samma sträng som användes för att generera den aktuella filtered-listan, utför den den kostsamma performFilteringLogic-beräkningen och skickar resultaten med updateFilteredResults.

const MyComponent = () => {
  const dispatch = useDispatch();
  const state = useSelector(state => state);
  const words = useSelector(state => state.something.words);
  const filtered = useSelector(state => state.something.filtered);
  // I would store an extra property alongside the filtered results that stores the string of words which were used to generate them
  // We can use this to prevent the effect from running unecessarily, like when the component is mounted
  const filteredBy = useSelector(state => state.something.filteredBy)

  useEffect( () => {
    // don't filter again on the same word list
    if ( words !== filteredBy ) {
      const filtered = performFilteringLogic(state, words);
      // We would pass both the filtered results and the words so that words can be saved to `filteredBy` in the reducer
      dispatch(updateFilteredResults(filtered, words));
    }
  }, [words]);

  return (
    <>
      <input value={words} onChange={(e) => dispatch(updateFilteringWords(e.currentTarget.value))}/>
      <SomeComponent data={filtered}/>
    </>
  )
}