Memoization for Better Reactjs Application Performance

Eyas Mattar
3 min readMar 4, 2020

--

Reactjs, Memo, JavaScript, Prformance, Closure and Reselect

The most important key concept to consider when developing a High-Quality web application is, as we all know, Performance.

In order to achieve high performance and good User Experience, we need to optimize our computation time in the application.

Assume we wrote the most optimized algorithms and got the lowest Time Complexity, we still can optimize our computation times by applying the Memoization technique.

Memoization is the process where we can remember previous computation, so whenever we ask for this previous computation result, we won’t compute it again, we just return it.

Assumption: The computation function should be Pure, thus, for every set of arguments S we will get the same result X.

Full Advanced React Course on Udemy

Memoization Example — Reselect

When developing React Redux applications, sometimes we need to apply some computation → map, join, reduce, or any other complex and expensive computation, on our data that is stored in the state.

In order to avoid repeated computations when our React Component re-renders:

const mapStateToProps = (state) => ({
userData: selectUserData(state)
});

function mapDispatchToProps(dispatch) {
return {
....
};
}

const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);

export default compose(
withConnect,
)(UserProfile);

Every time UserProfile component renders the mapStateToProps will execute the selector selectUserData

and will compute the data whether the selectUserData dependencies/arguments changes or not.

In order to avoid this we can use Memoization and we can remeber our last computation, so we re-compute only when the dependencies/arguments of selectUserData changes.

const selectUserData = (state) => {
const userProfile = selectUserProfile(state);
const musicBox = selectMusicBox(state);
const locations = selectLocations(state);
....
..
return result;
}

the dependencies are selectUserProfile selectMusicBox selectLocations

if these dependencies won’t change our result will be the same.

reselect is offering a Memoization feature that will store every time the arguments of the selector, and whenever we call the selectUserData, reselect will check if the argument changed (shallow equality) and decide whether to compute or to return the previous result.

const makeSelectMenu = () =>
createSelector([selectUserProfile, selectMusicBox, selectLocations],
(profile, musicBox, locations) => {
....
..
return result;
});

Writing our own Memoization

We can achieve this feature in many ways, I found using closures is the most convenient way.

function createMemo(func) {
let args = null;
let prevArgs = null;
let result = null;

return (...deps) => {
prevArgs = args;
args = deps;
// if our arguments didn't changed
if (args === null || prevArgs === null ||
(args.length !== prevArgs.length) ||
!shallowEquality(args, prevArgs)) {
// call function
result = func.apply(null, deps);
}
return result;
};

}

We will create a wrapper function that will return our actual function (closure), in order to store args prevArgs result .

In every call of the inner function we check whether the args and prevArgs are different or not.

Usage

const ourMemo = createMemo((state) => {
console.log('executed...')
return state.num;
})

const result = ourMemo(state)
console.log(result)

setTimeout(() => {
state = {
num: 130
}
const result = ourMemo(state)
console.log(result)
}, 4000 )

setTimeout(() => {
state.num = 130
const result = ourMemo(state)
console.log(result)
}, 4000 )

logs:

executed...
120
executed...
130
130

Conclusion

When our application scales, due to many page features and due to data scaling, computation time levels will be the main difference between Low-Quality and High-Quality application.

More Advanced Concept

Webpack 5: Complete Developers Guide

--

--

No responses yet