Reputation: 821
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
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.
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.
state.xyz.filteredResults
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)
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 ) )
}
}
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