MichaelRazum
MichaelRazum

Reputation: 821

Redux react root reducer slowing down

I‘m building an application using react and redux. One functional feature is to filter results based on the state. So in each class Component I had something like this:

filteredResults = this.filterResults(this.props.value1, this.props.value2,...)

Now I thought that there should be a performance gain if I just add filteredResults to the redux state and introduce a RootReducer. Actually, that slowed down the application. Any Idea why that happened? For me, it is a bit counterintuitive since filteredResults is calculated right now many times.

PS: This is how my RootReducer looked like:

import {_getFilteredResults} from "../components/utils";


const createFilterRootReducer = reducer => (state, action) => {
  let reduced_state =  reducer(state, action);
  let filteredResults = _getFilteredResults(reduced_state.value1, reduced_state.value2, reduced_state.value3, reduced_state.results);
  return {...reduced_state, filteredResults:filteredResults}
};


export default createFilterRootReducer;

That was applied on myRootReducer

const rootReducer = combineReducers({
    searching: ReducerSearching,
    roomOption: ReducerZimmer,
    maxPrice: ReducerMaxPrice,
    minPrice: ReducerMinPrice,
    internalMaxPrice: ReducerInternalMaxPrice,
    Address: ReducerAddress,
    Results: ReducerResults,
    activePage: ReducerActivePage,
    favoriteResults:ReducerLikedItems,
    resultSort: ReducerSort,
    acceptedCookie: ReducerCookies,
    headerHeight: ReducerHeaderHeight,
    mapView: ReducerMapView,
    rehydrate: ReducerRehydrate

});
export default createFilterRootReducer(rootReducer);

One more thing: I was also using 'redux-persist' to persist the state!

Upvotes: 1

Views: 303

Answers (1)

ivan
ivan

Reputation: 56

Well, it would be helpful if you could provide some additional infos and the code you are using.

I believe there are at least 3 ways to do what you want to achieve.

1. Use createSelector from reselect

The easiest way in my opinion is having a selector to calculate the filtered results based on the actual state of your whole results and the filtering values.

import { createSelector } from 'reselect';
const getResults = state => state.results; // e.g. array of strings
const getFilter = state => state.filter; // e.g. an input value 

export const filteredResults = createSelector( getResults, getFilter, ( results, filter ) => {
    return results.filter( item => item.indexOf( filter ) != -1 );
});

Now you can provide filteredResults via connect and mapStateToProps to your components without worrying of filtering your results every time.

Note that createSelector is smart enough to perform an equality check on its parameters in order to recompute the function only when any of its parameter changes.

Pros

  1. You don't need to store the filtered results in the state tree

Cons

  1. Since the filtered results are not stored in the tree, you will need to import this selector everywhere you want to access this data.
  2. You won't be able to access it in a direct way e.g. state.xyz.filteredResults

2. Store the filtered results in the tree when storing anything that would change them.

You can calculate and store the filteredResults in the same tree branch where ( and when ) you store the results and the input value. This is possible only if your original results are store in the same location.

const initialState = {
    results: [],
    filteredResults: [],
    filter: ''
}

const filterResults = ( results, filter ) => results.filter( item => item.indexOf( filter ) )

const myReducer = handleActions({
    [SET_RESULTS]: (state, action) => ({
        ...state,
        results: action.payload,
        filteredResults: filterResults( action.payload, state.filter )
    }),
    [SET_FILTER]:  (state, action) => ({
        ...state,
        filter: action.payload,
        filteredResults: filterResults( state.results, action.payload )
    })
}, initialState)

Pros

  1. You will be able to manually access your filteredResults since they are stored in the tree.

Cons

  1. You will need to make sure every action changing anything realted the the filtered results will update them.
  2. You will be storing extra data in the store ( results + filtered results ).

3. Use Redux-Thunk and store the filtered results

This is a different approach that would work. Basically you can store the filtered results in a different part of the tree when you input, but in order to do so, you will need Redux Thunk middleware

export const onChangeInputValue = ( value ) => {
    return (dispatch, getState) => {

        const results = getResults( getState() )
        const filteredResults = results.filter( item => item.indexOf( value ) )

        dispatch( setInputValue( value ) )
        dispatch( setFilteredResults( filteredResults ) )
    }   
}

Pros

  1. you can store the filtered results in a different part of the tree to where the results or the input value are stored.

Cons

  1. you need an extra action for storing the filtered results.
  2. you are dispatching 2 actions separately that might cause an extra rendereing.

Final consideration

You can choose what is the most suitable and viable way depending on your needs.

I personally like to use the selectors (1) and recompute data since that allows me to separate the logic from the actions and reducers.

Upvotes: 2

Related Questions