Is putting functions in render like this a bad practice?
Are these functions created every render, leading to a performance hit? How big of a performance hit? Is there a link somewhere that measures this or a reliable source saying that this is bad?
Does this cause unnecessary renders in any way? I hear this is especially not a good idea for PureComponent.
Having trouble finding clarification whether this is okay or not.
class MyComponent extends Component {
...
render() {
const myFunc1 = () => {};
const myFunc2 = () => {};
const myFunc3 = () => {};
const myFunc4 = doSomething(() => {});
return (
<div>hello world</div>
);
}
}
Is putting functions in render like this a bad practice?
Yep
Are these functions created every render, leading to a performance hit?
Yes
How big of a performance hit?
It can vary from totally negligible (most of simple components) to absolutely critical (if you constantly re-render your whole tree on a complex app).
Is there a link somewhere that measures this or a reliable source saying that this is bad?
See Chase DeAnda's answer.
Does this cause unnecessary renders in any way? I hear this is especially not a good idea for PureComponent.
Indeed, since the function will be created at every render, if you pass them to a PureComponent, it will think the function changed and will render again.
Having trouble finding clarification whether this is okay or not.
I hope this helps.
More like a bad practice. It won't break your code, but can cause some serious performance issues. From the React Docs
The problem with this syntax is that a different callback is created
each time the component renders. In most cases, this is fine. However,
if this callback is passed as a prop to lower components, those
components might do an extra re-rendering. We generally recommend
binding in the constructor or using the property initializer syntax,
to avoid this sort of performance problem.
So what you have in the example is fine because you aren't passing it down as a prop. If you passed myFunc4 to the div as a prop, then you would see some performance issues.
Is putting functions in render like this a bad practice?
Well it certainly isn't good practice, but from a performance perspective it's entirely project-dependent.
Are these functions created every render, leading to a performance hit? How big of a performance hit?
Yes, the functions are created on every render call, and if you are doing anything slightly more complex than this year's 600th job-search toy and you are calling your render repeatedly and rapidly (e.g. in a 60 FPS render cycle), the newly created function objects will have a minor impact on performance as the garbage collector will have to run more frequently.
This is again project-dependent and opinion based, but we have a rule to prefer creating non-primitive objects outside any render calls (or any lifecycle methods that are directly associated with render, such as componentDidUpdate).
Does this cause unnecessary renders in any way?
Simply creating function objects inside render won't cause additional unnecessary renders by itself, but as I mentioned, the function objects are created with every render call, which can be more frequent than you might've expected at first.
If you are also passing down such a function object as a callback or handler to a child component, remember that shouldComponentUpdate implementations which do a shallow reference check on props will trigger, because with each render you are passing down a different function object reference.
Related
I’ve read the documentation about memoization techniques in React and have a feeling that I understand them correctly.
However, I see projects where useMemo and useCallback are applied in different ways.
I would like to confirm that I’m not missing anything, so I will describe my understanding below. Please, tell me if this makes sense or correct me if I’m wrong :)
useMemo
This hook is used to cache the result of some calculation between re-renders.
const cachedValue = useMemo(() => calculateValue(), dependencies)
It means that after the first run, the cachedValue is saved in memory and returned on subsequent renders without any calculations if dependencies remain the same. The cachedValue object on the will be the same (same reference) and would return true on Object.is().
Memoizing results of expensive computations improves performance because they are not calculated even if the component is re-rendered, but at the cost of running the hook itself. (Not sure about that)
Additionally, it is used to prevent re-renders when passing these values as props to a component memoized with React.memo(Component) or as dependencies to useEffect hook.
useCallback
This hook caches the function definition (not the result of its work) between re-renders.
const cachedHandler = useCallback(() => { doSomething(); }, dependencies);
The function is defined and saved (but not called) in the variable on the first run, and the same definition is returned on subsequent re-renders if dependencies remain the same.
Since the function is not called when this hook is used and no results are cached, there are no immediate performance gains. (Not sure about that)
The benefit is only when this cached function is passed as a prop to a component memoized with React.memo(Component) or as a dependency to useEffect.
When a memoized function is passed as a prop inside an inline function (<Component onSomething={() => cachedHandler()} /> there are no performance gains since a new anonymous function is created and passed every time. (Saw this usage a few times).
React.memo
A component wrapped in React.memo is cached and skips re-renders as long as its props remain the same. There is no need to memoize primitive values (strings, for example), but for objects and functions, you need to apply either useMemo or useCallback hooks.
General notes
A lot of projects and developers wrap values and functions in these hooks prematurely either by convention or as a precaution ("if we memoize everything, there is no need to think about it in the future and the cost is not so high").
The documentation and React developers advise against using these hooks until it is necessary (gives measurable performance gains) as using them is not free and adds cognitive complexity (harder to read and follow the code).
This description does not cover all the cases, but can be applied to most simple cases.
I am recently refactoring a web app using React Hooks. I encounter a problem regarding useCallback. Based on description of Kent: https://kentcdodds.com/blog/usememo-and-usecallback, useCallback is to pass in identical function reference to sub-components, to avoid re-render of sub-components, so that the performance is better. However, it's used together with React.memo. And as Kent said:
MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. In fact, the need to optimize stuff with what I'm about to show you is so rare that I've literally never needed to do it ...
So, my question is: am I right to claim that we do not need to use useCallback usually? except when the callback is expensive to create, using useCallback avoids re-creating the callback for every render.
Say, for a onClick or onChange event handler, 2 lines or less, shall we just not use useCallback to wrap it?
I find the useCallback() is necessary when I don't want the function reference to change. For example, when I'm using React.memo on some child component that should not be re-rendered as a result of a reference change in one of its methods that comes through props.
Example:
In the example below Child1 will always re-render if Parent re-renders, because parentMethod1 will get a new reference on every render. And Child2 will not re-render, because the parentMethod2 will preserve its reference across renders (you can pass a dependency array to make it change and be re-created when new input values come).
Note: Assuming the Child components are being memoized with React.memo()
function Parent() {
const parentMethod1 = () => DO SOMETHING;
const parentMethod2 = useCallback(() => DO SOMETHING,[]);
return(
<React.Fragment>
<Child1
propA=parentMethod1
/>
<Child2
propA=parentMethod2
/>
</React.Fragment>
);
}
On the other hand, if the function is expensive to run, you can memoize its results using the useMemo hook. Then you will only run it when new values come, otherwise it will give you a memoized result from previous calculations using those same values.
https://reactjs.org/docs/hooks-reference.html#usecallback
useCallback
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
useMemo
Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
I think you are right. From how it's designed, useCallback should be almost useless in React. It can not be used directly to prevent a child render.
What can save a child render is to wrap entire render using a useMemo.
const Title = () => {
...
const child = useMemo(() => {
return <Child a={"Hello World"} />
}, [])
return (
<>
{child}
<div onClick={onClick}>{count}</div>
</>
)
}
The above approach is a bit different than React.memo because it acts directly on the parent Title, instead of Child. But it's more or less answer your question why it's useless, except when you use it as the shortcut for useMemo.
Article explaining this, https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981
back to useCallback
Now let's go back to see if a callback wrapped with or without useCallback is useful.
<div onClick={onClick}>kk</div>
The only thing it might save is that when it's under reconciliation, onClick (with useCallback) points to the same function instance.
However I don't know if React actually does any optimization at that step. Because assigning a different callback to the attribute might take additional memory and time. But adding a new variable in general takes additional memory as well.
So this type of optimization is more like a coding optimization, more or less subjective. Not objective enough to be applied in a solid case.
Of course, if you want to fix a function instance for any third party function, ex. debounce. That might be a good use, but still smell fishy, because useMemo seems much more versatile to cover this case as well.
All in all, I'm only pointing out, useCallback isn't doing what's the public believe it can do, such as to bailout child component.
I've been building quite an extensive app with React, Redux and Normalizr but have been struggling with a totally excessive number of redundant render cycles, and am now thinking I've perhaps misunderstood how to combine mapStateToProps and Normalizr.
In my mapStateToProps, I'm referencing all of a certain entity type, which I can then use to get an entity from its ID:
function mapStateToProps(state) {
return {
allMilestones: state.account.entities.milestones,
allTasks: state.account.entities.tasks,
}
}
Using the above mapStateToProps, whenever I need to get an entity, I can just go (for example) const taskObject = this.props.allTasks[taskId]. Quick and convenient, but I understand this means there's a lot of props being passed around - I thought passing around references wouldn't be a big deal, but then I noticed everything was re-rendering just, all the time.
Would this be a likely culprit for an unreasonable amount of render cycles?
If so, could I expect implementing Reselect for all of these cases would positively affect performance?
It's possible but this is a guesstimation without benchmarks (and you should try to clarify that yourself before making any optimisations). React-Redux will do a comparison of all of the keys of the object returned from mapStateToProps and will rerender if any are different.
If the reference changes each time - because you're using an immutable Map, for example - then the component will re-render any time mapStateToProps() is called.
You should try to make the return value more stable by only returning what is required by the connected component.
https://react-redux.js.org/using-react-redux/connect-mapstate#return-values-determine-if-your-component-re-renders
I've recently decided to refresh on react's documentation and came across https://reactjs.org/docs/react-api.html#reactmemo (React.memo).
At first glance it looked like a good candidate for pretty much all of my function components. So I did just that and applied memo on all of them.
I'm not sure if I am able to notice dramatic performance boost, but it for sure didn't make anything slower.
Hence the question: should I apply memo to all my fucntion components by default, just in case so I can catch edge cases of re-rendering a lot of elements? Are there any drawbacks to this / something else i need to be aware of?
No, you shouldn't memoize all your function components by default. You should decide when to do this on a case-by-case basis.
1. It's only for "pure" function components (as in, without side effects)
From the documentation:
If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result.
Imagine that you add some side effect to your function component, for example, reading the current time from Date.now(). The component will work as expected the first time it's called with certain props, but the next time it's called with those same props, the old time will be used. This can lead to confusing bugs.
2. Not all pure function components will benefit from memoization
For example, if it's unlikely a component will ever be called with the same props, then memoizing the result will likely lead to worse performance. After all, memoization is just storing the result of a function call to an object, where the key is derived from the input to the function and the value is the result of the function call. Why store this if you'll never use it?
3. Memoization is more useful for functions that are computation intensive
If your function component doesn't take many CPU cycles to render, then any performance optimization is likely going to be negligible. But it's now possible to later add side effects to that component, forget that it's being memoized, and waste time fixing the resulting bugs.
Again, from the docs:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
For shorter explanation:
If React.memo() everything makes React more efficient and optimized, it should be considered as a default behavior, not only an option (like pure function).
Basically, React.memo() prevents redundant rerenders by comparing the component's previous and current props.
<Parent>
<Child />
</Parent>
Normally, when the Parent rerenders, the Child rerender as well by default.
With React.memo(), if the props passed into Child are not changed when Parent rerender, Child does not rerender.
The question here is: why does React know props passed to Child was not changed in the current rerender?
The answer is: React itself has to do a comparison, and it is the cost of React.memo()
If the time to comparison > the time to just rerender the whole Child component, it even slows down your app.
I would like to perform state validation on react component on every setState call and if new state is invalid, use another - valid state.
For example, if I have slider, with 4 pages and I want to do
this.setState({
page: 5
});
In my validation function I'd check whether new state's value is greater than pages count and if so, I'd set 4 (total pages) instead of provided 5.
Is this possible?
Thanks
There doesn't seem to be a great answer for this, after trying many things.
If there is an answer to the question it might be that there's no good way to do it. I think it doesn't hurt to look at potential solutions though and the drawbacks of each one.
Solution 1
Use componentWillUpdate. This is deprecated.
Solution 2
Validate state on render - but in your case and in most others, validation may lead to setState, which react tells you never to do in render(), but doesn't go into any detail about what will actually happen. Of course, you would have to be careful to prevent infinite loops, but if you call setState in render() upon some condition which cannot logically result in an infinite loop, then what unintended side effects could occur?
Solution 3
As above, make a wrapper function to both validate and set state. But this is no good in my opinion because any state changes done with set state directly cannot be validated. Not only does setState directly not call your validation, but your validation also does not run on the items in the initial state. You might say, why would you give your component an initial state that's invalid, but I don't care, if I want to validate state I want it to validate 50000% of the time and no less.
In a complex form example, there are other issues with this. What if the validity of one field depends on the state of another field? Surely then, a function to re-validate a field when it changes is not enough. Some people will say to test the field being passed in and dispatch multiple events for re-validation, but again, this just isn't acceptable to me. Its not fullproof, it can lead to bugs, and make things hard to test.
Solution 4
Use componentWillReceiveProps. Not only is this deprecated, but its also static, meaning that you don't have access to the component instance. For some components this is fine because you can still modify the state that is returned, but if your component has data outside of state and props then you cannot access it, in addition to not being able to use non-static methods, which in my opinion makes this method pretty useless in many situations.
Solution 5
Haven't tested this one (bear with me, its fairly nuts), but you can override setState function in your component. Insert a callback into every setState instance. Track how many times setState is called and how many times the setState callback is called. Inside the callback, you can check the two counters stored in the object to see if the last callback is currently running, then, if it is, run one function which validates all state. On render, set both of the counters back down to zero. I don't recommend this by any means. It may work but its a pretty major hack.
Solution 6
You can use componentDidUpdate, but this is inefficient. You will be blindly setting state with a value that was not validated then letting your component render only to validate it after and possibly call render again. Once again you have to be careful to avoid infinite loop while calling setState in componentDidUpdate. I read elsewhere that at least if you set the state here, the DOM will only re-draw once. In other words, it doesn't draw immediately after render() but waits until componentDidUpdate is triggered and re-calls render if state changes in there. This seems like maybe its the only solution I know about that react only warns about without telling you explicitely not to do this. In your case, the efficiency does not matter, but what if the state you were trying to validate was a 100 field form with a very expensive render method. Now, on every key down you've essentially doubled the amount of work that react has to do to render your components, causing them to first render without being validated, and then filtering and/or validating them for a very likely second render.
Additional Issues
In my case, i'm working on a form and not a simple component like you described above. I may go with a mix of solution 3 and 5. Its more complicated then I want though. It involves using one function to filter, generate errors, and set the state for each field (ie. solution 3). And then on componenetDidUpdate I may look for state keys whose values are different from the last value that the field was validated with. The second step here has many ways of doing so, all of which are messy and inefficient. To make it worse, they practically never run under normal circumstances because I always use the first function to update and validate their state, so this makes testing it harder. It's like leaving some fallback in my app that seems to work but during the entire development process it never triggers except for like the one time that I decided to test it. Seems like a bad idea to me.
There are additional efficiency concerns I also won't get into which relates to trying not to re-validate a field if it has the same value it was validated with last time. This is a whole other can of worms, but basically what it boils down to is that you should be able to not only validate a state before render, but you should have access to the previous state as well, so you can save some cpu time and only validate the fields that changed. For example, if you have a very long textarea with some complex regex, it would be nice to not validate that on every component render even if its a separate field that is changing.
Afterthoughts
I'm very thoroughly disappointed that react seems to provide no legitimate option for this. Seeing as you can call setState many times in one operation, and each one will be queued, how is it that they don't provide us with one callback after all state changes are resolved and we're about to render? Well if you think about it, the render function itself is this callback, because I think its only ever called after setStates are resolved, but again, its evil to call setState on render, and it would be a million times cleaner to have render() simply receive the correct state, so I don't see how this is very useful.
Does anyone know why they decided to get rid of componentWillUpdate?
Yes it is certainly possible. Simply wrap setState so that you can do your validation prior to the actuall call. Something like:
function checkThenSet(newState){
// do validation here
if (validation) {
this.setState(newState);
} else {
// validation failed
this.setState(otherState);
}
}
And if your updating your state by way of user interaction, then your render function would look like this
var MyComponent = React.createClass({
checkThenSet: function(newState) {/* Same as above */},
render: function() {
return (<input
type="text"
value={this.state}
onChange={this.checkThenSet}
/>);
}
});
I'm not sure, but shouldComponentUpdate method might help you.
class Example extends React.Component {
constructor(){
super();
this.state={
count: 0
}
this.handleClick = this.handleClick.bind(this);
this.reset = this.reset.bind(this);
}
shouldComponentUpdate(nextProps,nextState){
return nextState.count <= 4
}
handleClick(){
const newState = this.state.count + 1;
this.setState({count: newState})
}
reset(){
this.setState({count: 0});
}
render(){
return <div>
<p>State: {this.state.count}</p>
<button onClick={this.handleClick}>Click Me</button>
<button onClick={this.reset}>Reset</button>
</div>
}
}
React.render(<Example />, document.getElementById('container'));
But it also depends on your logic whether component should be updated. fiddle example
I hope it will help you.