React - memo on event props
In this article, we would like to show how to create memo on props that uses event methods in React component.
The solution presented in the article shows how in an easy way, we can prevent components' unnecessary re-rendering cycles when we pass methods in props to components. In the solution, it is enough to add $
prefix before prop name to memorize component for that method.
In practice, that solution can be used to memorize event props.
The example shows 2 buttons:
- the first one is re-rendered by changed
onClick
method, - the second one is not re-rendered with changed
$onClick
method.
Note: by "changed
onClick
method" we understand: each time new arrow function is created that forces child component re-rendering.
Practical example:
// ONLINE-RUNNER:browser;
// Note: Uncomment import lines while working with your project.
// import React from 'react';
// import ReactDOM from 'react-dom';
const wrapProps = (state, props) => {
const wrapper = {};
const keys = Object.keys(props);
for (const key of keys) {
const value = props[key];
if (key[0] === '$') {
if (typeof value === 'function') {
let name = key.slice(1);
let pipe = state[name];
if (pipe) {
pipe.method = value;
} else {
pipe = state[name] = {
method: value,
proxy: (...args) => pipe.method?.(...args)
};
}
wrapper[name] = pipe.proxy;
} else {
throw new Error('Incorrect function property name prefix usage.');
}
} else {
wrapper[key] = value;
}
}
return wrapper;
};
const useWrapper = (props) => {
const [state] = React.useState({});
return wrapProps(state, props);
};
const pipe = (component) => {
const WrapperA = React.memo(component);
const WrapperB = (props) => {
const wrapper = useWrapper(props);
return React.createElement(WrapperA, wrapper);
};
WrapperB.displayName = `Pipe(${component.displayName || component.name})`;
return WrapperB;
};
// Usage example:
const Button = ({text, onClick}) => {
console.log(`Button rendering (${text}).`);
return (
<button onClick={onClick}>{text}</button>
);
};
const PipedButton = pipe(Button);
const App = () => {
const [counter, setCounter] = React.useState(0);
return (
<div>
<div>Value: {counter}</div>
<PipedButton text="with onClick event" onClick={() => setCounter(value => value + 1)} />
<PipedButton text="with $onClick event" $onClick={() => setCounter(value => value + 1)} />
</div>
);
};
const root = document.querySelector('#root');
ReactDOM.render(<App />, root);
Summary:
$onClick
prop uses memo on function, preventing component against unnecessary re-rendering.
Hint: the truth is:
$
added to prop, creates proxy method that is never changed later, preventing component against unnecessary re-rendering.