Reducing React table rerendering - reactjs

I have a table with two columns, each of which are editable. The table also includes the ability to add new entries. The problem I'm running into is that whenever a single row is edited, each row is re-rendered. For larger tables this causes noticeable keystroke delays when editing a single row. Any ideas as to how this can be prevented?
I haven't been able to replicate the performance problems in a "small" example, but https://codesandbox.io/s/zen-williams-r8pii?file=/src/App.js is the basic functionality. I've tried dropping React.memo in a few places to no avail (I'm a newbie at this stuff)
Thanks for any help!

There were some things that were making the row rerender:
First, the row component needed to be wrapped in React.memo
const ThingRow = React.memo(({ thing, onUpdate }) => {
...
})
Then the onUpdate needed to be memoized as well with React.useCallback, and to reduce the dependencies in useCallback to only setThings(see note), you can use the callback version of setThings and .map to update the item
const ListOfThings = ({ things, setThings }) => {
const onUpdate = React.useCallback(
(thing) => {
setThings((prevThings) =>
prevThings.map((t) => (t.id === thing.id ? thing : t))
);
},
[setThings]
);
return (
<table>
<tbody>
{things.map((thing) => (
<ThingRow key={thing.id} thing={thing} onUpdate={onUpdate} />
))}
</tbody>
</table>
);
};
When having this kind of issue you need to look for the way to memoize props you are passing down, especially callbacks, and reduce dependencies in hooks
this is the codesandbox fork https://codesandbox.io/s/table-row-memoization-xs1d2?file=/src/App.js
note: according react docs
React guarantees that setState function identity is stable and won’t change on re-renders

There are a couple of optimizations you can try.
First of all, if you are dealing with a lot of data, the core problem could be that you are rendering too many things to the DOM.
If this is the case, I would start by adding virtualization or pagination to the table.
You could easily add virtualization with a library, e.g. using FixedSizeList from react-window:
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);
const Example = () => (
<List
height={150}
itemCount={1000}
itemSize={35}
width={300}
>
{Row}
</List>
);
This should already cut down on any performance problems, but if you only want to optimize rerenders:
The default behavior for react is to render the current component where a useState setter was called, and all child components, even without any props changing.
so you should wrap both ListOfThings and ThingRow in memo to opt out of this behavior:
const ListOfThings = memo({ things, setThings }) => {
...
});
const ThingRow = memo(({ thing, onUpdate }) => {
...
});
If you don't do this, changing the unrelated title input will lead to rerendering everything.
Lastly, you are passing an inline event handler (onUpdate) to ThingRow.
This will create a new function object for each rendering of ListOfThings, which would bypass the memo optimization we did earlier.
To optimize this, you could create a persistent reference to the function with useCallback:
onUpdate={useCallback(
thing => setThings(prev => prev.map(t => t.id === thing.id ? thing : t)),
[setThings])}
Note that it's considered best to use the function update style in useState whenever you are relying on a previous value for the update.
Small disclaimer: If you are having performance problems, I would go only with the virtualization / pagination solution, and not try at all to optimize for rerenders. React's default rendering behavior is very fast, and changing it could potentially introduce hard to find bugs related to things not updating.

Related

Prevent re-rendering when child components list expands

In the following example, every click of the button should only render the next Number component, but instead it re-renders the whole list again. I would like to know the best practice to prevent the previous one from re-rendering. Thank you.
const Number = (props) =>{return (<div> {props.number} </div>)}
const NumberListUpTo = () =>{
const [listLength, setListLength] = useState(1);
return(
<>
<button onClick = {() => setListLength(listLength + 1)}>add one more</button>
{
Array.from({length: listLength}, (v, i) => i).map(
(i) => (<Number number = {i} />))
}
</>)
}
Use React.memo to memoize your components.
You should always provide a key for React components when you are rendering an array of elements.
Edit:
As React documentation says:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
Do not try to prevent renders or memoize every component. Try to optimize your code whenever you there is a performance issue. Optimization in React, using memo, useMemo, useCallback, etc) comes at a price; they take resources and may even slow down your app.

is useCallback a performance boost for the react app?

could someone explain to me if the use of useMemo and useCallback is expensive or cheap? I've checked the React's source code and I think they are cheap to use, but I've also read some people saying they are not.
In a Next.js context using useCallback:
const MyComp = () => {
const router = useRouter():
const handleClick = useCallback(() => router.push('/some-path'), [router]);
return <button onClick={handleClick} />
}
vs plain arrow function here:
const MyComp = () => {
const router = useRouter():
return <button onClick={() => router.push('/some-path')} />
}
Am I saving re-renders with useCallback?
The cost of memoize and comprare the dependencies array [router], is more expensive?
Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.
Someone knows why?
Also checking this component render:
import {useCallback, useState} from "react";
let i = 0
const CallbackDemo = () => {
const [value, setValue] = useState('');
console.log('render', i++);
const handleClick = useCallback(() => setValue(Math.random()), []);
return (
<div>
<span>{value}</span>
<button onClick={handleClick}>New set</button>
</div>
);
}
export default CallbackDemo;
I saw the same count of renders using or not using useCallback
Am I saving re-renders with useCallback?
Not in that specific case, no. The useCallback code does let you save roughly the cost of an assignment statement because it doesn't have to update the click handler on the button element, but at the cost of a function call and going through the array of dependencies looking for differences. So in that specific case, it's probably not worth doing.
If you had a more complex child component, and that component was optimized not to re-render when called with the same props (for instance, via React.memo or React.PureComponent or similar), or you were passing the function to a bunch of child components, then you might get some performance improvements by not making them re-render.
Consider this example, where simpleClickHandler is always recreated but memoizedClickHandler is reused¹ via useCallback:
const { useState, useCallback } = React;
const Parent = () => {
const [counter, setCounter] = useState(0);
console.log("Parent called");
const simpleClickHandler = () => {
console.log("Click occurred");
setCounter(c => c + 1);
};
const memoizedClickHandler = useCallback(() => {
console.log("Click occurred");
setCounter(c => c + 1);
}, []);
return (
<div>
Count: {counter}
<Child onClick={simpleClickHandler} handlerName="simpleClickHandler">simpleClickHandler</Child>
<Child onClick={memoizedClickHandler} handlerName="memoizedClickHandler">memoizedClickHandler</Child>
</div>
);
};
const Child = React.memo(({handlerName, onClick, children}) => {
console.log(`Child using ${handlerName} called`);
return (
<div onClick={onClick}>{children}</div>
);
});
ReactDOM.render(<Parent/>, document.getElementById("root"));
<div id="root"><?div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Note that when you click, only the child being passed simpleClickHandler re-renders, not the one being passed memoizedClickHandler, because memoizedClickHandler's value is stable but simpleClickHandler's value changes every time.
Using useCallback requires more work in the parent (checking to see if the previous function can be reused), but may help save work in child components.
It's important to note that the stability useCallback (and useMemo) give you are only appropriate for performance reasons, not correctness. React can throw away the previous copy of a handler and new a new one if it wants to, even if the deps haven't changed. If you need a correctness guarantee (the result definitely 100% will not change unless the deps change), you have use a ref. But that's a very rare use case.
Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.
Someone knows why?
Primarily because NaN === NaN is false, because all comparisons with NaN are false. So if they used ===, any deps array containing NaN would always be considered different from the previous one. But using Object.is avoids that problem, because Object.is(NaN, NaN) is true.
¹ Note that "reused" here means: a new handler is created every time, just like simpleClickHandler, but useCallback may return the previous handler you've given it if the deps haven't changed, so the new one is thrown away. (JavaScript engines are really quick at allocating and reclaiming short-lived objects.)
useCallback like useMemo indeed improve performance
but!!! you don't need to use it to everything because it will make your website slower.
this is better for the heavy lifting components like for charts and stuff like that.
that consume a lot of resource and take a lot of time to process and this will make this specific component load much more faster and not stuck everytime you do a change.
you can see deep compression like this:
react useEffect comparing objects
this link will show you for instance how to do a deep compression for use effect

React Performance issues in passing callback as props in functional components

React doc
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
React doc :-
The problem with this syntax is that a different callback is created each time the LoggingButton 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 class fields syntax, to avoid this sort of performance problem.
This is fine for class based component, what about functional component.
Can we use like below and experience performance issues like it stated because different callback is created each renders?
It says it works most of the cases, so I am not sure if we should leave it as it is or use React hook useCallBack for all functional components with callbacks.
I have got 3 diff types
const LoggingButtion = (props)=>{
const [ loggedStatus, setLogStatus] = useState(false);
const handleClick =()=> {
console.log('Button Clicked');
setLogStatus(true)
}
return (
<button onClick={() => { console.log('button clicked'); setLogStatus(false)} > // Type1
<button onClick={() => handleClick()}> // type 2
<button onClick={handleClick}> // type 3
Click me
</button>
);
}
Can we use like below and experience performance issues like it stated because different callback is created each renders? It says it works most of the cases, so I am not sure if we should leave it as it is or use React hook useCallBack for all functional components with callbacks.
It only matters if the app is large enough that it's causing performance issues, which isn't likely. Avoiding useCallback makes the code easier to read and will work just fine in 97% of cases. But if you do find a situation where child components are re-rendering too often, and you have enough of those components that the performance impact is visible, go ahead and use useCallback:
const handleClick = useCallback(() => () => {
console.log('Button Clicked');
setLogStatus(true)
}, []);
But, note that you don't have child components here, only <button>s. While a different event listener has to be attached to each element each render, that's not likely to be a problem either.
Feel free to do what you're currently doing, without useCallback, and only change to useCallback if re-rendering proves to be a problem.

How to update state from the components in React?

I'm working on the "Orders list" page.
I have a hook:
const [orders, setOrders] = useState([])
What happens currently:
when page is loaded - I fetch orders with Apollo useQuery
in the useEffect: once I get orders, I set them with setOrders
then, I return them with:
{orders.map((order) => (
<OrderComponent order={order}/>
))}
This works fine for displaying data, but I would like to be able to change order data (just state) from OrderComponent.
So let's say we have a notes input in the OrderComponent:
<input type="text" name="order_notes" value={order.notes}/>
The question is:
How to handle such updates from the child component?
What I tried:
On the parent page, I created handler:
const handleOrderNotes = ({ value, orderIndex }) => {
const ordersCopy = [...orders]
ordersCopy[ordersIndex].notes = value
setOrders(ordersCopy)
}
then I passed it to the OrderComponent along with orderIndex:
{orders.map((order, index) => (
<OrderComponent order={order} orderIndex={index} handleOrderNotes={handleOrderNotes}/>
))}
And added the handler to the input:
<input
type="text"
name="order_notes"
value={order.notes}
onChange={(value) => {
handleOrderNotes({value, orderIndex})
}}
/>
And it worked just fine. But after I started adding more and more stuff, to the point where I would like to use components in OrderComponent. I started to wonder if there is a better way of doing such things?
During the devlopment I was often getting errors like:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
So I believe that I'm missing something crucial here.
Just getting started with React, so would apprentice any input. Thanks :)
There are two ways I can think of to solve this. You would have to chose which one suits you based on how your app is organized/designed.
If using redux or some kind of state management for app, then you don't need to tell parent component but as you have index of the order with you in the child component itself, you can simply update that order in the app store/state itself.
You may want to wrap each component with useMemo and use useCallback for passing any callback to these child components. This second step you should do even if you are have state management done for your app to avoid unnecessary re-renders
Please change this
{orders.map((order, index) => ( <OrderComponent order={order} orderIndex={index} handleOrderNotes={handleOrderNotes}/> ))}
to
{orders.map((order, index) => ( <OrderComponent order={order} orderIndex={index} handleOrderNotes={({value,index})=>handleOrderNotes({value,index})}/> ))}

Is there a reason I have to export React components as JSX or can I export them as objects that contain the JSX and other things?

TL;DR: I'm wondering if a solution I've thought of will cause performance issues.
The title is a bit confusing so let me clarify. A standard way of defining my header button component would be something like this (the code is using typescript but it's not relevant to the problem so you can probably ignore it):
import React from "react";
const HeaderButton = (props: Client.Common.Header.HeaderButton.Import) => {
const service = useHeaderButton();
return <div>
//header component JSX here
<div>
};
export default HeaderButton;
I want to change it up a bit. I've found I would much rather be able to expose some internal component methods to the parent component. In this case, I would like to be able to provide a toggle method to the parent instead of using an "active" property to determine the active state of the button. My reasoning is that I'd rather avoid having to set up the toggle logic in every parent component I use my HeaderButton in if I can instead define it in my button component and then have the parents use that method.
I've done this and it works as I'd like it to (so far, at least). I'm relatively new to both React and programming in general and self taught so I have gaps in my knowledge. I'm not that knowledgeable about React under the hood and how it does its performance optimizations etc so I'm worried I've done something that will cause unpredictable issues. Here's what I've done:
//Header.tsx (this component is using the regular style)
import React, { useEffect } from "react";
import "./Header.scss";
import HeaderButton from "./Components/HeaderButton/HeaderButton";
const Header = () => {
const HeaderButtonService = HeaderButton({ renderProps: <div>TEST</div>, class: "languageSelectionButton" });
useEffect(() => {
setTimeout(() => {
HeaderButtonService.toggle();
}, 5 * 1000);
}, []);
return (
<div id="headerBar">
<div className="headerNavigationButtonsContainer"></div>
{HeaderButtonService.view}
</div>
);
};
export default Header;
//HeaderButton.ts
import HeaderButtonView from "./HeaderButtonView";
const HeaderButton = (props: Client.Common.Header.HeaderButton.Import) => {
const service = useHeaderButton();
return {
toggle: service.toggleActive,
view: HeaderButtonView({ ...props, service.active })
};
};
export default HeaderButton;
//HeaderButtonView.tsx
import React from "react";
import "./HeaderButton.scss";
const HeaderButtonView = (props: Client.Common.Header.HeaderButton.Import) => {
return (
<div className={"headerButton" + (props.active ? " active" : "")
+ (props.class ? " " + props.class : "")}
style={{ ...props.style }}>
{props.renderProps && props.renderProps}
</div>
);
};
export default HeaderButtonView;
In my solution, I import HeaderButton.ts instead of HeaderButton.tsx. The parent component passes the relevant props to HeaderButton.ts which passes them down to HeaderButtonView.tsx while adding the "active" prop which it gets from a custom hook, not the parent component. It then returns the result of invoking HeaderButtonView with these new props as well as the method to toggle the active state.
This is a simple example but I would potentially use this template to expose state values and multiple methods to parent components.
The code works, it renders what I want it to render and toggles the active state after 5 seconds.
My concern is that, not knowing all that much about how react works under the hood, I might have created an optimization problem. Is there any reason I shouldn't be using this pattern?
I have done some testing and it doesn't seem to break anything. I added a counter to the state of header.tsx and increment it every 3 seconds then watch for re-renders. I was concerned that react would not be able to recognize that the old and new HeaderButton components are the same but it did. Though react goes through the component tree, it doesn't re-render the button (except after the first 5 seconds when activity is toggled).
Also, should HeaderButton.ts be a hook? It's working as intended atm so I'm not sure what exactly I gain/lose from adding "use" in front of it.
Your approach here is 1) contrary to how 99% of people use React, 2) contrary to very way React is intended to be used, and 3) overcomplicated in a way that adds a level of abstraction to React that absolutely does not need to be there.
1) This is just not how people write React code. Sure it might work for you and make sense on some level but no one else follows this pattern. What about when you start importing and using other people's components? What about when you have to partner with someone else to write an app? What about when you hand off or are handed off a bunch of code that is patterned in a completely different way? There is absolutely a lot to be said for following prevailing (or at least common) coding patterns because it makes your code a lot more interoperable and easy to understand compared to the rest of the framework ecosystem, and vice-versa.
2) React at its very core is intended to be declarative. It is the number one adjective most people would use to describe it, and features heavily on the very front page of the React website. Your proposed pattern here is very un-declarative, and directly defeats not just the declarative nature of React but the inherent patterns of component state and props. I could link you to examples in the documentation as to how declarative coding, state management, and props feature heavily in React design patterns but the list would include practically every page in the website: Components and Props, State and Lifecycle, Lifting State Up, Thinking in React, etc etc.
3) Your proposed pattern is just... needlessly complicated and abstract. It adds a layer of confusion that does not actually make things easier. I can barely follow even your basic minimal example code!
Your core rationale seems to be this:
My reasoning is that I'd rather avoid having to set up the toggle logic in every parent component I use my HeaderButton in if I can instead define it in my button component and then have the parents use that method.
That's a great instinct - make things reusable and modular so that you don't have to repeat yourself too often. Well, you can do that beautifully in React while adhering to the tenants of React's declarative nature!
First let's rewrite your components in a way that is a more "traditional" React style - just make the <HeaderButton> a regular component that accepts an active prop, and storing that state in the parent, <Header>. This is called "lifting state up" and is a key concept in React - state should live at the lowest common denominator that allows the necessary components access to it. In this case the parent <Header> needs access to the state, because it needs to not only pass it into <HeaderButton> as a prop, but be able to modify that state:
const HeaderButton = ({active}) => {
return <div>{active ? 'Active' : 'Inactive'}</div>
};
const Header = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
return (
<header>
<button onClick={toggleActive} >Toggle active state</button>
<HeaderButton active={active} />
</header>
);
}
Cool, now state lives in the parent, in can modify that state, and it passes that state as a prop to <HeaderButton>. It is very declarative, easy to understand, and it's clear where state lives and what component is rendering what.
Now on to your concern about reusing the toggle logic. What if we want to use <HeaderButton> somewhere else and have the same toggle logic? What if we want to have five header buttons inside of <Header>? Do we need to copy and paste the same logic many times?
React provides a great solution here with custom hooks. Custom hooks allow you to encapsulate logic and state in a clean way. And - this is very important - the state it encapsulates still lives inside of the component that calls the custom hook. This means we can encapsulate the state and logic but they will still "live" inside of <Header> so we have access to it to pass as a prop. Let's try it:
const useHeaderButtonState = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
return [active, toggleActive];
}
const HeaderButton = ({active}) => {
return <div>{active ? 'Active' : 'Inactive'}</div>
};
const Header = () => {
const [active, toggleActive] = useHeaderButtonState();
return (
<header>
<button onClick={toggleActive} >Toggle active state</button>
<HeaderButton active={active} />
</header>
);
}
Now, the state and the toggle logic live inside of useHeaderButtonState(). When called, it returns both a value (active) and a function for updating that value (toggleActive). Inside of <Header>, we can deconstruct the result of the custom hook call and use it to render.
We could even extend this custom hook even further to return not just the state and updater function, but a component to render. Then, if we want to render multiple instances of a component, including all its associated state and logic, and still have access to the state and logic in the parent component (<Header>), we can do that:
const useHeaderButtonState = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
const headerButtonComponent = (
<>
<button onClick={toggleActive}>Toggle active state</button>
<HeaderButton active={active} />
</>
);
return [headerButtonComponent, active, toggleActive];
};
const HeaderButton = ({ active }) => {
return <div>{active ? "Active" : "Inactive"}</div>;
};
const Header = () => {
const [
headerButtonComponent1,
active1,
toggleActive1
] = useHeaderButtonState();
const [
headerButtonComponent2,
active2,
toggleActive2
] = useHeaderButtonState();
const [
headerButtonComponent3,
active3,
toggleActive3
] = useHeaderButtonState();
return (
<header>
{headerButtonComponent1}
{headerButtonComponent2}
{headerButtonComponent3}
</header>
);
};
https://codesandbox.io/s/pensive-sutherland-2onx9
Now we're cooking with gas! We're reusing state and logic but doing it in a declarative way that makes sense inside of React.
Sorry to be so heavy handed but I really, really want to discourage you from using the imperative pattern you proposed above. I've been writing React code for 3+ years and trust me when I say that sticking to the established patterns of React will pay off in the long run. Not only is it legitimately easier to write and comprehend, if will help your career to write code that other devs can more easily understand and work with.
I conduct a lot of hiring interviews and if I saw someone submit the code you wrote above, I would think they have no idea how React works or is intended to work and would immediately disqualify them. If you find it difficult or counterintuitive to understand, I would suggest keep on learning and practicing (with a more declarative React-compatible style) until it clicks. Otherwise, perhaps React just isn't the framework for you and you'd be best served with a different framework that more closely matches your preferences, style, and mental models!
Good luck!
Edit: Oh and one last thing I'll touch upon. You mentioned concerns about performance. In this case the performance differences are actually completely negligible and not worth even considering. In general React is very well optimized on its own and you don't need to worry about performance except in very specific edge cases. You should typically only optimize if and when you actually run into a performance bottleneck, and you solve for that. As they say, premature optimization is the root of all evil.
My response instead addresses the core programming pattern that you are proposing here on the basis that it makes the process of developing, debugging, and understanding the code itself needlessly difficult.

Resources