I've recently learned that passing down object literals or functions as props can cause unnecessary re-renders. As I am doing an audit on my app, I am finding some cases where I have common components that have callbacks on events that do different things. It's unclear to me what the most elegant way to handle this would be.
So for example in my app I have a component called SharedBtn that is used all over the app multiple places and in large loops. This button has an onClick listener. But in every instance this onClick is used we are passing down a different function to do a different thing every time.
Example:
https://codesandbox.io/s/k31120vnyo
I read this related article with examples. But their solution is to move the onClick logic to the shared component. This would be ugly for me as it is used in many different spots with many different handlers. How could I have the multiple click handlers without moving the click handling logic to the SharedBtn component itself?
You could create a small wrapper component for each special instance.
class IndexSharedButton extends React.PureComponent {
handleClick = e => {
this.props.onClick(e, this.props.index);
};
render() {
return <SharedBtn copy={this.props.copy} onClick={this.handleClick} />;
}
}
class AnimalSharedButton extends React.PureComponent {
handleClick = e => {
this.props.onClick(this.props.animal, this.props.type);
};
render() {
return (
<SharedBtn copy={this.props.animal} onClick={this.handleClick} />
);
}
}
You could also manually manage caching the bound handlers, but IMO that's pretty ugly and much less idiomatic.
Also, I think it goes without saying that you shouldn't worry about this much at all if you haven't measured it to be an actual issue. Re-renders don't happen that often, and with shouldComponentUpdate should happen even less. Optimizations like this add more code for little benefit.
Most of the time the performance hit is negligible. But the correct way to mitigate this in the age of hooks is via useCallback.
import { useCallback } from "react"
const MyComp = () => {
const func = useCallback(() => doSomething(), []);
return <OtherComp func={func}/>
};
Related
Can't find any recent official info if any of the three options below is allowed?
constructor(props) {
this.state = {
item: <SomeItem />,
item1: () => <SomeItem />,
item2: SomeItem,
};
}
I found this answer but it references an old link from web archive which says:
What Shouldn’t Go in State?
...
React components: Build them in render()
based on underlying props and state.
But that link doesn't say why that is a bad idea, if it will introduce bugs, etc.
This is a really good question.
The reason that putting components in state is advised against is just that it goes fundamentally against the React model, which is that a component provides a render method (which is a pure function) that the React engine uses to automatically update the DOM to reflect the values of the component's props and state.
The output of that render, i.e. the React Element, is supposed to be used directly by the React engine. The contract is that your app, and all its components, generate a bunch of Elements in a pure way for the React engine to manage.
By doing things like introducing side effects in render, or putting the Elements in state, you're essentially breaking the 'pure' contract and it may give unpredictable results, which may or may not be considered bugs in your application. The specifics of the bugs may even change with different versions of React, with different engine implementations. The point is that you're breaking the React contract, so whilst it may work in some cases, it also may not in others or even the same cases as React itself changes. The behaviour is not guaranteed.
React has built-in ways to cache renders based on prop values, like React.memo, that the engine provides and understands, and are part of the contract. If you want to cache render output for performance reasons, this is the way to do it.
Indeed, this is exactly why such functions are provided by the React API rather than just letting you do it yourself.
At the end of the day, React component instances are just objects, and you can store objects in state, so it shouldn't cause any trouble if you avoid pitfalls. One such pitfall is that if you're creating handlers to put on their props, those handlers will close over the context in which they're created, which may lead to some unexpected outcomes. Here's an example of that:
const {useState, Fragment} = React;
function Thingy({onClick}) {
return <div onClick={onClick}>A</div>;
}
// STALE CLOSURE
function Example() {
const [value, setValue] = useState(0);
const [comp, setComp] = useState(
<Thingy onClick={() => { console.log("A: value = " + value); }} />
);
const handler = () => {
setValue(v => {
++v;
console.log("B: value = " + v);
return v;
});
};
return <Fragment>
{comp}
<div onClick={handler}>B</div>
</Fragment>;
}
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
This is the classic stale closure thing. It's probably a bit easier to do accidentally using functional components and hooks (as I did there) rather than class components, but it's definitely possible to do with class components as well.
But if you're not doing that (either not creating functions for the component you're storing, or creating ones that don't use anything they close over that may change), it should be fine.
But look at React.memo, which may be a better answer depending on what your reason for wanting to put component instances in state is.
You can do something like this, if I understand you right
const Title = () => {
return <h1>Hello CodeSandbox</h1>;
};
class App extends React.Component {
state = {}
constructor(props) {
super(props)
this.state = {
item: function() {
return <Title />;
}
};
}
render() {
return (
<div className="App">
{this.state.item()}
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
export default App;
You can do it, but it's a bit strange. A React element is an object like any other. In this case, it will be the result of a method call:
// `<SomeItem/>` compiles to
React.createElement(SomeItem, null);
// A similar `React.createElement("div", null)` becomes
const el = {
$$typeof: Symbol(react.element),
key: null,
props: {},
ref: null,
type: "div",
_owner: null,
}
It's strange because it's unnecessary (and a little confusing). You can just generate the element (including any state or props updates) whenever you need it.
There's also a risk that you break one of the core guarantees of React: elements are immutable. Storing the element like this gives you a chance to mutate it and thus confuse React.
If you need many copies of the same element then it may be slightly more performant to keep it like this, especially if it is expensive to generate.
If declaring an arrow function inside a react class component is bad for performance,
why does declaring variables(arrow functions) inside a functional component have better performance?
The way React is now moving towards using the entire app with only functional components, won't it have performance issues for big apps that have long component trees with lots of variables that being redeclared on each update? (I know some are garbage collected but the declaration still occurs, same as using arrow function inside the render function of a class component)
class ReactComponent extends React.Component {
render() {
return (
<div onClick={() => console.log('do something')}>
<SomeOtherComponent onChange={() => console.log('pass function')} />
</div>
);
}
}
const functionalComponent = () => {
const doSomething = () => console.log('do something');
const passFunction = () => console.log('pass function');
return (
<div onClick={doSomething}>
<SomeOtherComponent onChange={passFunction} />
</div>
);
};
Nothing is "bad for performance", there are only unnecessary computations. If creating a function is necessary to express the behaviour of a program, there is nothing "bad". It is only "bad" if creating the function can be avoided, and it is even worse if a lot of computations can be avoided.
Creating a function is cheap. It creates a new function object, that contains a reference to the functions code as well as to it's environment (the "closure"), so a function expression is not much different than an object literal. Therefore creating functions is not "bad", and it never was.
The real problem that arises from function expressions in React, is not the function expression itself, but the logical result of it:
<div onClick={() => doStuff()}> Test </div>
On every rerender, a new function object gets created, that references a different environment, that might contain a different doStuff method, that might do something different. Therefore on every rerender, React has to detach the listener, and attach a new one referencing the new function, so that the new behaviour gets triggered. For one listener this is cheap, but if you pass down functions to other components, that pass down functions to other components, the cost multiplies (= more components have to rerender), so it might get expensive (= something we might worry about). If the function changes, but what it does does not, reattaching the listeners is uneccessary, and thus it can be avoided by making sure that functions only get recreated when the values they depend on get recreated:
const callback = useCallback(() => doStuff(), [doStuff]); // returns the same function, unless doStuff changes
<div onClick={callback}> Test </div> <- only gets reattached when doStuff changes
If declaring an arrow function inside a react class component is bad for performance
The reason people warn you about creating functions on every render is not because creating functions is slow. It's not; creating functions is very fast.
The performance issue comes when you pass that function to something else, and that something else is using shouldComponentUpdate or PureComponent or React.memo or useMemo or some other form of memoization. Since it received a new function, it thinks it needs to recompute, and so the memoization benefit is lost.
It's true that this issue can occur in function components too, but that's one of the things that useCallback and useMemo are for. You can use those hooks to create the function only once, and thus you will not needlessly break memoization of other components.
I just read Ract doc on Passing Functions to Components, it seems using bind and arrow function would both break React's performance optimization.
For example,
class Foo extends React.PureComponent {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
}
}
class Foo extends React.PureComponent {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
}
What I know is both of them are creating new callback function when render method is called.
For the child component Button, would it cause extra rendering as I
suppose React.PureComponent and React.memo are using reference check
to see if props are changed?
If so, would the extra rendering happen on both React's virtual DOM
and the actual DOM?
Based on my experiments (basically using setInterval to recreate the callback function), it seems that the React virtual DOM has extra rendering since the render is called with extra times. However, the actual DOM seems not having extra rendering. Not sure if it is because the callback function is not part of the DOM.
I'm also wondering the common solution to prevent such performance issue. One possible solution I thought of is to cache the callback so we don't need to bind them again and agin. Is there any better solution?
With react hooks now available should I in case of functional components wrap every function passed with props with useCallback and every other props value with useMemo?
Also having custom function inside my component dependent on any props value should I wrap it with useCallback?
What are good practices to decide which props or const values from component wrap with this hooks ?
If this improves performance why not to do it at all times ?
Lets consider custom button where we wrap click handler and add custom logic
function ExampleCustomButton({ onClick }) {
const handleClick = useCallback(
(event) => {
if (typeof onClick === 'function') {
onClick(event);
}
// do custom stuff
},
[onClick]
);
return <Button onClick={handleClick} />;
}
Lets consider custom button where we wrap click handler and add custom logic on condition
function ExampleCustomButton({ someBool }) {
const handleClick = useCallback(
(event) => {
if (someBool) {
// do custom stuff
}
},
[someBool]
);
return <Button onClick={handleClick} />;
}
Should i in this two cases wrap my handler with useCallback ?
Similar case with use memo.
function ExampleCustomButton({ someBool }) {
const memoizedSomeBool = useMemo(() => someBool, [someBool])
const handleClick = useCallback(
(event) => {
if (memoizedSomeBool) {
// do custom stuff
}
},
[memoizedSomeBool]
);
return <Button onClick={handleClick} />;
}
In this example I even pass memoized value to useCallback.
Another case what if in the component tree many components memoize same value ? How does this impact performance ?
Not worth it, for multiple reasons:
Even official docs say you should do it only when necessary.
Keep in mind that premature optimization is the root of all evil :)
It makes DX (developer experience) far worse: it's harder to read; harder to write; harder to refactor.
When dealing with primitives (like in your example) memoizing costs more CPU power than not doing it. A primitive value doesn't have a concept of references, so there's nothing to memoize in them. On the other hand, memoization itself (as any other hook) does require some tiny processing, nothing is for free. Even though it's tiny, it's still more than nothing (compared to just passing a primitive through), so you'd shoot your own foot with this approach.
To put all together - you'd waste more time typing all the hooks than a user would gain on having them in the application if you want to put them everywhere. The good old rule applies: Measure, then optimize.
I agree with the principles proposed by #jalooc
To give some more insight about the exhibited use cases in the OP, here is my advice:
Expensive children renders
function Component() {
const callback = useCallback(() => { dostuff }, [deps])
return <Child prop={callback} />
}
The above makes sense if Child is a very expensive component to render. As such, it is probably exported like so:
function Child() {
...this takes significant CPU...
}
// Export as a pure component
export default React.memo(Child)
Expensive computations
function Component({ foo }) {
// This very expensive computation will only run when it's input (foo)
// changes, allowing Component to re-render without performance issues
const bar = useMemo(() => {
... something very complicated with `foo` ...
}, [foo])
return <div>{bar}</div>
}
Conclusion
Do what makes sense or that have measured bad performance
A function declaration inside a component changes at each render. If this causes derived expensive computations, memoize it (useCallback) or move it outside the scope.
If a component itself is expensive to render, make it pure with React.memo, with the help of #2 if necessary
If something IS itself expensive to re-compute, memoize it (useMemo)
Do what makes sense or that have measured bad performance
I'd argue that there's nothing wrong with putting useCallback everywhere you create a function. The same points are applicable to useMemo; but I'll be mentioning only useCallback since, for the sake of brevity.
As a concession, I must mention that this isn't a cornerstone in your development process, and you must proceed with a solution that your team is comfortable with, whether it's using useCallback or not.
My main argument for using such memoization hooks "extensively" is that you don't have to think of potential performance issues on the front of "reference update-induced" rerendering if you do it. Fewer things to think about = good. More time and energy to solve the actual problems.
Many say "premature optimization is bad". Well, cargo cult isn't good either, and this citation is a pure example of such, taken too far out of context, and with a premise of having an authority behind it. here used to be a good summary on that, unfortunately, the account is taken down currently, but it's available on archive.org.
The concerns of "premature optimization" in this case are structural changes to the code and impaired readability/write-ability.
Structurally, these changes are for the better, making you separate your components.
Readability-wise, there's an extra wrapper, but that comes together with strict tracking of your function dependencies, which otherwise is done implicitly. Dependencies become explicit as they should be since in this system they play a too important role to swipe it under the rug. Readability, therefore, only wins. Although it's harder to write now, the "ritual" is always the same. Readability is more important than the convenience of writing a function.
(Just make sure you use eslint to track your dependencies; it could be a headache when you forget to add a dependency and it gets cached.)
So, "write now, optimize later" - thanks, I'd pass. To me, it's a negligibly cheap optimization that is justified enough and is reasonable enough, if your team is ready to accept the arguments above. It's also fine if they aren't: the topic itself isn't something to die for. It's just a small quality-of-life tool.
I'm trying to understand stateless components and what the difference is between these examples:
class App {
render() {
return (
<div>
{this.renderAFunction('hello')}
</div>
);
}
renderAFunction(text) {
return (
<p>{text}</p>
);
}
}
and this:
class App {
render() {
return(
<div>
<RenderAFunction text='hello'/>
</div>
);
}
}
const RenderAFunction = ({text}) => (
<p>{text}</p>
);
Or if there is any difference at all?
Functionally, there is absolutely no difference. Both end up rendering a paragraph element, but there are other aspects to consider. There are three points to make (in my opinion) when examining both methods:
Reusability: You have to understand to separate components when you need to. If renderAFunction is just meant to generate some JSX based on, for example, an API request, then it's fine being in a method. But if you want to reuse it somewhere else, then separate it into it's own component. A huge part of React is component reusability and getting rid of code duplication. Separating the method into it's own component would be imperative to accomplish this.
Purpose: There are reason to use stateless function components and reasons not to. The whole point of stateless functional components is to not have state and be presentational. If you need to do something that involves the React lifecycle or internal state, keep it as a method, or new class depending on if you want it reusable.
Performance: Using a stateless functional component would be less efficient. This is because it's a component, not just some JSX returned from a method. As of right now, the React team plans on making some optimizations for stateless functional components because they do not contain state and are merely presentational, but this probably won't happen until after React Fiber is done and thus your stateless functional component has no optimizations versus a regular full-fledged class component. That makes it incredibly inefficient versus a method returning some JSX, especially if it's just used once in another component.
A good rule of thumb is to ask yourself, do I need it anywhere else? If not, then keep it in a method. If you don't need it anywhere else, separating the JSX into a separate component would have worse performance and wouldn't follow React's core principles.
If you are going to need it somewhere else, then separate the component so you follow React's concept of reusability.
Your App's render function will be translated into following JS code for your first example:
render() {
return React.createElement(
'div',
null,
this.renderAFunction('hello')
);
}
And the following one for the second one:
render() {
return React.createElement(
'div',
null,
React.createElement(RenderAFunction, { text: 'hello' })
);
}
While they both looks almost the same, there is one significant difference: laziness. React will execute RenderAFunction body only in case it gonna be mounted to the DOM hierarchy.
It is insignificant is your case, but imaging following example:
const LazyApp = () => {
const heavy = <div><HeavyStuff /></div>
// ...
return <div>done it</div>
}
const HardWorkingApp = () => {
const heavy = <div>{HeavyStuff()}</div>
// ...
return <div>done it</div>
}
const HeavyStuff = () => {
// ... do heavy rendering here
}
Lazy app will not perform any heavy stuff at all. Maybe it is not very clear from that synthetic example why one would do anything like that at all. But there are real-world examples out there, when something conceptually similar (creating Components without rendering them) is happening.
Basically, both will serve the same purpose. But the real advantage of creating a component is its reusability.
If you have to render that particular piece of jsx just for this component. Then, there is no need to create a separate component.