How We Reduced Our Re-Renders by 80% With React Redux and Custom Context - Console - Blog
How We Reduced Our Re-Renders by 80% With React Redux and Custom Context - Console - Blog
blog
Introduction
In the project I’m currently working on for a client (a global denim brand), my collegues
and I started to notice performance degradation in our React app, which mostly consists
of complex forms and different kinds of input components. Specifically, we’re working on
the webshop checkout, a pretty crucial part in any webshop. Performance started to get
so bad after a while that basic input fields couldn’t keep up with fast typing. This was
annoying at first, but soon required action.
When we started looking into the cause of these performance issues, we found that most
React components in the form would re-render whenever any form input value changed.
We knew that this was caused by poor (un-optimized) handling of user events and data
flow. Some of our components were hundreds of lines of JavaScript long - a red flag on
it’s own - and had dozens of props that were being passed down multiple levels deep.
This can impact performance as well, and debugging such components is no fun either.
This is also often referred to as ‘prop-drilling’.
Digging a little deeper, we looked into our JavaScript performance using Chrome’s JS
Profiler. Here we noticed that some frames would take up to 250-1000ms to render,
when ideally you’d want that to be under 16.6ms for your browser to be able to render
the app at 60fps.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 1/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
When we measured how many times our Input component would render during a
normal user interaction flow, we saw that in some cases the component would re-render
up to 800-1300 times, whoops!
This might seem obvious, but if you notice that your machine is struggling with the app
you’re building, it’s safe to assume that the experience for users on lower-end devices is
going to be horrible. This especially becomes a problem when you’re working on a multi-
language, globally used web app for example. As developers we’re usually working on
fast Macbook Pro’s (or other up-to-date hardware), I’m using a 6-Core Intel Core i7 with
16GB of RAM for reference. It’s good to keep in mind, because performance in React
apps doesn’t seem to become a problem that much these days.
Let’s dive into the approach we took to optimize the performance of our React
application!
Before we started, we made a summary of things that we knew would improve overall
performance. Not all of these improvements (such as using React.memo ) are covered in
this post, but as you’ll see I’ve tried to cover the most important parts.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 2/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
TL;DR - Results
We’ve measured a typical user flow / journey through the React checkout application,
filling in a few forms, etc.
Chrome’s JavaScript Profiler visualises this nicely. These screenshots show the before
and after of a typical user flow, filling in the form in 20-30 seconds. Additionally, it shows
the TBT (Total Blocking Time). TBT measures the total amount of time that a page is
blocked from responding to user input, such as mouse clicks, screen taps, or keyboard
presses (https://web.dev/lighthouse-total-blocking-time/).
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 3/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
You’ve got a React app with an existing global store (this is optional). I’ve included a
global Redux store just to illustrate that we can use multiple stores next to each other.
There’s a good chance that you, or your company or client uses Redux already anyway.
Here we’ll focus mainly on the components rendered inside <App /> , I don’t really care
about the rest of the app, this might have been written by someone else, so preferably I
don’t even need to touch it.
index.jsx
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 4/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root"),
)
Quick example of a store that might exist inside your app already.
globalStore.js
/**
* Define and export action types.
*/
export const INIT_APP = "INIT_APP"
export const SOME_GLOBAL_UPDATE_FORM_ACTION = "SOME_GLOBAL_UPDATE_FORM_ACTION"
export const SOME_GLOBAL_RESET_FORM_ACTION = "SOME_GLOBAL_RESET_FORM_ACTION"
/**
* Example 'global' Redux store
* that might exist in your app already.
*/
const initialState = {
initialized: false,
forms: {},
someUserData: {},
// ... etc
}
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 5/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
/**
* A standard reducer function.
*
* @param {object} state
* @param {object} action
* @returns {object} nextState
*/
const reducer = (state = initialState, { type, payload }) => {
switch (type) {
case INIT_APP:
return {
...state,
initialized: true,
}
case SOME_GLOBAL_UPDATE_FORM_ACTION:
return {
...state,
forms: { ...state.forms, [payload.formId]: payload.values },
}
case SOME_GLOBAL_RESET_FORM_ACTION:
return {
...state,
forms: Object.fromEntries(
Object.entries(state.forms).filter(([key]) => key !== payload.formId),
),
}
// ... etc
default:
return state
}
}
In case of the project I’m working on we’re dealing with ‘dynamic’ forms which can be
configured in our CMS. These forms contain a lot of logic (too much IMHO), but these
are real-world scenarios we must deal with in larger codebases.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 6/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
Here you can see that we render the App component with some children, and when the
app renders for the first time it dispatches an example action INIT_APP to the ‘global’
Redux store. This is just to illustrate what your current app might look like.
Imagine that the three FormContainer components contain lots of logic and different
child components, their configuration is determined dynamically using the formId value
for example.
App.jsx
/**
* App component with some example form components.
*
* @returns {React.FC}
*/
const App = () => {
const dispatch = useDispatch()
const isInitialized = useSelector((state) => state.initialized)
useEffect(() => {
if (!isInitialized) dispatch({ type: "INIT_APP" })
}, [dispatch, isInitialized])
return (
<div className="App">
<h1>React + Redux app</h1>
<hr />
<FormContainer formId="homeBillingForm" />
<FormContainer formId="homeShippingForm" />
<FormContainer formId="guestLoginForm" />
</div>
)
}
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 7/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
Presentational components can be tested with ease. Simply mock the required props
and you’re good to go.
You should be able to write these components without using the return keyword, as in
the following examples. I personally try to do no logic at all inside presentational
components.
Form.jsx
/**
* Form presentational component.
*
* @returns {React.FC}
*/
const Form = ({ children, ...props }) => <form {...props}>{children}</form>
FormInput.jsx
/**
* Example input presentational component.
*
* @returns {React.FC}
*/
const FormInput = ({ children, ...props }) => (
<div className="FormInput">
<input type="text" {...props} />
{children}
</div>
)
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 8/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
context.js
The FormContainer component is just a regular component where the business logic of
the form is defined. This is the job of a container component. It uses a regular
useDispatch to dispatch actions to the ‘global’ store, and it uses some custom hooks
(https://reactjs.org/docs/hooks-custom.html) that we’ll define later.
FormContainer.jsx
/**
* Form container component.
* In this component we will handle business logic.
*
* @returns {React.FC}
*/
t F C t i ({ f Id hild }) {
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 9/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
const FormContainer = ({ formId, children, ...props }) => {
/**
* We're still free to use the 'global' dispatch function wherever we like.
*/
const dispatch = useDispatch()
/**
* Get a function from a custom hook which we'll write later on
*/
const { setFormId } = useFormActions()
/**
* Get values from the form store using our
* custom Redux hook 'useFormSelector'.
*/
const values = useFormSelector(valuesSelector)
const requiredFieldsFilled = useFormSelector(requiredFieldsFilledSelector)
useEffect(() => {
setFormId(formId)
}, [setFormId, formId])
if (requiredFieldsFilled) {
dispatch({
type: SOME_GLOBAL_UPDATE_FORM_ACTION,
payload: { formId, values },
})
}
},
[dispatch, requiredFieldsFilled, formId, values],
return (
<Form onSubmit={onSubmitHandler} {...props}>
{children}
</Form>
)
}
/**
* Connected form container component.
* Connects the component to the form context.
* Thi ll th f t R d h k
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 10/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
* This allow the use of custom Redux hooks.
* https://react-redux.js.org/next/api/hooks#custom-context
*/
const ConnectedFormContainer = connect(null, null, null, {
context: FormContext,
})(FormContainer)
/**
* Form container provider.
* Provides a Redux store for each form instance and renders the connected form conta
* https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-cont
*
* You can provide an optional parameter 'store' which can be useful when writing tes
* (not covered in this blog post)
*
* @returns {React.FC}
*/
const FormContainerProvider = ({ store, children, ...props }) => {
/**
* Create a new store for every instance of FormContainerProvider.
* 'useMemo' makes sure we create the store only once before the component will mou
* 'useRef' makes sure we get a consistent reference to the store object.
*/
const formStore = useMemo(() => store || createFormStore(), [])
const { current } = useRef(formStore)
return (
<Provider context={FormContext} store={current}>
<ConnectedFormContainer {...props}>{children}</ConnectedFormContainer>
</Provider>
)
}
/**
* Export the form container provider instead of the form container component.
*/
export { FormContainerProvider as default }
Use the connect HOC (higher-order component) function to specify which context
the FormContainer component needs to connect to.
Pass the custom context and a Redux store as props to a default Redux Provider
and wrap the connected component to create a FormContainerProvider that
provides the current store to any child components.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 11/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
Now a FormInput component can reliably retrieve values from the store of the form it’s
currently rendered in, even though the component itself is used across different forms.
🤯
I wasn’t aware that you could pass in an options object as the fourth argument to the
connect function! We can simply use null for the first three arguments because we
don’t need to use mapStateToProps , mapDispatchToProps or mergeProps . We want to
get values using Redux selector hooks instead of mapping state variables to component
props. This was common practice when we were working with class-based components,
but nowadays we’d like to work with functions (hooks) only.
Now when we render three different instances of FormContainer for example, three
Redux stores will be created, each with the same initial state but unique internal state
over time.
In theory you could pass some initial state as props to the FormContainer component,
which you would pass into the createFormStore function if you want to set up the store
with initial values based on props.
It doesn’t matter how deep this component is nested inside the FormContainer .
Calling setValue will update a value in the current form store only, as described above.
No more hassle with mapStateToProps or mapDispatchToProps ! 😁
FormInputContainer.jsx
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 12/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
/**
* Form input container component.
*
* @returns {React.FC}
*/
const FormInputContainer = ({ name, children, ...props }) => {
/**
* ❌ creates a subscription and triggers a re-render on all updates to FormContex
*/
// const { someValue } = useContext(FormContext)
/**
* ⚠ creates a subscription to a single store value.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 13/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
*/
// const someValue = useFormSelector(state => state.someValue)
/**
* ✅ creates a memoized subscription to a single store value.
*/
// const someValue = useFormSelector(someValueSelector)
return (
<FormInput onChange={onChangeHandler} {...props}>
{children}
</FormInput>
)
}
We would need to validate input, write things to localStorage - but not when it’s a
password - etc.
utils.js
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 14/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
/**
* Just a single example of all the utility functions
* that we would use in our complex form.
*
* @param {string} value An input value
* @param {object} validationRule A dynamic validation rule
* @returns {boolean} isValid
*/
export const validateInput = (value, validationRule) => {
if ((value === undefined || value === "") && validationRule?.isRequired) {
return false
}
return true
}
Hooks.
React Redux exposes three useful - but not very well documented - functions, which we
can use to create custom Redux hooks.
createStoreHook
createDispatchHook
createSelectorHook
If we pass a context into these functions, we can create custom useDispatch and
useSelector functions, that only operate on the ‘local’ / ‘sub’ store, the form store in our
case.
In this post I’m creating a function useFormSelector , but if you’re creating complex
modals you could create useModalDispatch and useModalSelector for example - and
use them if you have a context + store set up as shown above in FormContainer.jsx .
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 15/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
hooks.js
/**
* Form reducer constants.
*/
const SET_FORM_ID = "SET_FORM_ID"
const SET_VALUE = "SET_VALUE"
const SET_VALIDATION_RULES = "SET_VALIDATION_RULES"
const RESET_FORM_STATE = "RESET_FORM_STATE"
/**
* Form reducer initial state.
*/
const initialState = {
formId: undefined,
values: {},
validityValues: {},
validationRules: {},
}
/**
* Another standard reducer function.
* This reducer will only handle form actions.
*
* @param {object} state
* @param {object} action
* @returns {object} nextState
*/
const reducer = (state = initialState, { type, payload }) => {
switch (type) {
case SET_FORM_ID:
return { ...state, formId: payload }
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 16/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
case SET_VALUE:
return {
...state,
values: {
...state.values,
[payload.key]: payload.value,
},
validityValues: {
...state.validityValues,
[payload.key]: payload.isValid,
}
}
case SET_VALIDATION_RULES:
return {
...state,
validationRules: { ...state.validationRules, ...payload },
}
case RESET_FORM_STATE:
return { ...initialState }
default:
return state
}
}
/**
* Redux form store factory function.
*
* Note that 'preloadedState' is not used in this example.
* It's useful when you want to provide a custom initial state
* when writing tests for example.
*
* @param {object} preloadedState Optional, default: undefined
* @returns {object} store
*/
export const createFormStore = (preloadedState) =>
createStore(reducer, preloadedState)
/**
* Rarely used hook for retrieving the form store directly.
* Preferably, use useFormSelector to access store values.
*/
export const useFormStore = createStoreHook(FormContext)
/**
* Form dispatch hook, similar to react-redux's useDispatch hook.
* Actions dispatched using this hook will only affect the specified context.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 17/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
*/
export const useFormDispatch = createDispatchHook(FormContext)
/**
* Form selector hook, similar to react-redux's useSelector.
* Use this hook to retrieve data from the form store.
*/
export const useFormSelector = createSelectorHook(FormContext)
/**
* Hook for convenient access to the form Redux actions.
*
* @returns {object} formActions
*/
export const useFormActions = () => {
/**
* Use useDispatch and useFormDispatch to be able to
* dispatch actions to both the form store and the global store.
*/
const dispatch = useDispatch()
const formDispatch = useFormDispatch()
/**
* Get (aka select) some values from the form store with 'useFormSelector'.
* It's no problem to use these hooks inside other hooks like this.
*/
const formId = useFormSelector(formIdSelector)
const validationRules = useFormSelector(validationRulesSelector)
/**
* Sets the form id.
*
* @param {string} id
*/
const setFormId = useCallback(
(id) => formDispatch({ type: SET_FORM_ID, payload: id }),
[formDispatch],
)
/**
* Sets a form value and does a validation check.
* We keep track of the value's validity using the 'validityValues' object.
*
* @param {string} key
* @param {string} value
*/
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 18/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
formDispatch({
type: SET_VALUE,
payload: { key, value, isValid },
})
}
[formDispatch, validateInput, validationRules],
)
/**
* Sets the validation rules.
*
* @param {object} validationRules
*/
const setValidationRules = useCallback(
(validationRules) =>
formDispatch({ type: SET_VALIDATION_RULES, payload: validationRules }),
[formDispatch],
)
/**
* Reset the entire form state in the current context.
* And - as an example - also update the global Redux store.
*/
const resetFormValues = useCallback(() => {
formDispatch({ type: RESET_FORM_STATE })
dispatch({ type: SOME_GLOBAL_RESET_FORM_ACTION, payload: formId })
}, [formDispatch, dispatch, formId])
return {
setFormId,
setValue,
setValidationRules,
resetFormValues,
}
}
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 19/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
Note, these selector functions can be used in both useSelector and useFormSelector !
I would recommend refactoring all existing selectors - even selectors for the ‘global’ store
- to reselect-like selectors.
Organize and create your store value selectors in a central location to be re-used across
multiple components. A great advantage of writing selectors like this with reselect is
that each selector is a ‘pure’ function. This makes sure your data is immutable and state
becomes predictable. It also prevents any potential side effects.
selectors.js
/**
* Creates a custom selector creator function.
* The resulting selector performs a deep equality comparison.
* Uses the 'isEqual' function from 'react-fast-compare'
* Useful for memoization of values of type object or array.
*
* The default 'createSelector' performs strict reference equality comparison (with '
*/
export const createDeepEqualSelector = createSelectorCreator(
defaultMemoize,
isEqual,
)
/**
* Composable memoized Redux selector functions.
* Syntax: createSelector|createDeepEqualSelector(...inputSelectors, resultFn)
* https://github.com/reduxjs/reselect
*
* Each selector must be a 'pure' function.
* A benefit of this is that it makes selectors very reliable and easily testable.
*/
export const formIdSelector createSelector(
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 20/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
export const formIdSelector = createSelector(
(state) => state?.formId,
(formId) => formId,
)
/**
* Before, we would calculate a value like this inside (multiple) components.
* In this new approach we can move the computation to a re-usable selector, like thi
*/
export const requiredFieldsFilledSelector = createSelector(
valuesSelector,
validationRulesSelector,
validityValuesSelector,
(values, validationRules, validityValues) =>
Object.keys(values)
.filter((key) => validationRules?.[key]?.required)
.every((key) => validityValues?.[key] === true),
)
You can imagine that a form store contains many properties, some of which you want to
combine to create new - more specific - values. Some components don’t need a
subscription to the entire formConfigs object in the following example snippet. It could
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 21/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
be a large object that some components actually do need to subscribe to, but others
might only need a subset of the data from that object.
Generally it’s a good idea to move as much logic to selectors when combining values
from the store. The reducer function preferably shouldn’t do much logic or
computationally expensive operations, as they will fire on every action of that type.
Selectors are a nice way to abstract away store-related logic in a clean and reusable
manner for consumers of store values. You could argue that it’s yet another Redux
boilerplate file + necessary imports, but I think it’s better to write selectors once instead
of re-declaring them in every component that needs that value.
Writing lots of selectors might seem stupid at first, but when selecting a value like
state.cart.cart.consumer.billingAddress.street in a few components, there’s a high
chance to make a small mistake and write
state.cart.consumer.billingAddress.street instead. This is a bug I actually ran into
while refactoring, it had gone unnoticed for almost 6 months.
Defining and having things in a centralised place prevents accidental typo’s and gives
the benefit of being able to make a single change to update all affected components.
selectors.js
/**
* Example of an polymorphic selector creator.
* (selector which you can pass an argument)
*/
export const createFormConfigSelector = (formId) =>
createDeepEqualSelector(
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 22/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
formConfigsSelector,
(formConfigs) => formConfigs?.[formId] || {},
)
/**
* Example usage of an polymorphic selector.
*/
export const formConfigShippingSelector = createDeepEqualSelector(
createFormConfigSelector("homeShippingForm"),
(formConfigShipping) => formConfigShipping,
)
/**
* This selector selects a value that has slightly different behavior
* as is often the case in more complex apps.
*/
export const formConfigBillingSelector = createDeepEqualSelector(
createFormConfigSelector("homeBillingForm"),
useShippingAsBillingSelector,
(formConfigBilling, useShippingAsBilling) =>
useShippingAsBilling ? {} : formConfigBilling,
)
/**
* Or:
*/
export const combinedFormConfigSelector = createDeepEqualSelector(
formConfigShippingSelector,
formConfigBillingSelector,
(...args) => Object.assign({}, ...args),
)
/**
* Transforming the selection even further:
*/
export const combinedFormConfigValuesSelector = createDeepEqualSelector(
combinedFormConfigSelector,
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 23/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
Object.values,
)
Feel free to skip this part, these examples show more advanced usage of selectors. You
probably don’t need this unless you’re dealing with a large, complex and performance
intensive app. We chose not to use re-reselect , so the results described in this article
are based on reselect selectors only.
/**
* This creates a selector that caches based on the 'prefix' value.
* So whenever this selector is called with a previously used prefix it will return t
* previously computed selection, instead of re-evaluating the example 'formatValues'
* The function determining which value the selector should cache on is referred to a
*
* Note that these variable names from the store are totally random and
* have nothing to do with the examples listed above.
*/
const formattedFormValuesSelector = createCachedSelector(
(state) => state.values,
(state, format) => format,
(state, format, prefix) => prefix,
(values, format, prefix) => formatValues(values, format, prefix),
)((state, format, prefix) => prefix) // Cache selectors by prefix
/**
* This is exactly the same.
*/
const formattedFormValuesSelector = createCachedSelector(
(state) => state.values, // a
(state, format) => format, // b
(state, format, prefix) => prefix, // c
(a, b, c) => formatValues(a, b, c), // resultFn
)((state, format, prefix) => prefix) // keySelector
/**
* Or:
*/
const formattedFormValuesSelector = createCachedSelector(
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 24/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
/**
* Customizing the caching implementation by specifying a custom cache object
* https://github.com/toomuchdesign/re-reselect/tree/master/src/cache#readme
*/
const formattedFormValuesSelector = createCachedSelector(
(state) => state.values,
(state, format) => format,
(state, format, prefix) => prefix,
formatValues,
)({
keySelector: (state, format, prefix) => prefix, // Cache selectors by prefix
cacheObject: new LruObjectCache({ cacheSize: 5 }), // Use the 'LruObjectCache' to c
})
The results
After the rewrite, which took approx. 2 weeks, we saw some nice results.
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 25/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
A good refactor once in a while can avoid a lot of ‘tech-debt’. This is especially true for
projects where developers come and go. Also it revives motivation to work in the
codebase which felt old and messy!
Conclusion
I hope these tricks can benefit you in your projects as much as it did in our case!
Comments:
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 26/27
2/24/2021 How we reduced our re-renders by 80% with React Redux and custom context - console.blog
LOG IN WITH
OR SIGN UP WITH DISQUS ?
Name
(https://github.com/woudsma)
(mailto:[email protected])
https://blog.tjerkwoudsma.com/2021/01/29/how-we-reduced-our-re-renders-by-80-with-react-redux-and-custom-context/ 27/27