React - use the exact same component instance many times in the DOM - javascript

I have a React component that's a div, some styling, and text. That exact component with the exact same value is used many, many times in the DOM. I am trying to figure out how to create just one instance of that component, and simply present the exact same instance everywhere that I need it. I tried use memoization, but that seems to be memoizing the instance itself.
Just to be explicitly clear, I'd like to memoize the component such that calling the function with the same arguments from anywhere will return the exact same instance every single time.
const Component = React.memo((value) => (<div>...</div>));
Calling this seems to memoize the particular instance, but it will still create a brand new instance every single time it's invoked from a different place in the Virtual DOM, even if the arguments are identical. As I understand it, React.Memo is just a wrapper around a particular instance.
I've thought of some hacky ways to do this, but wanted to ask before exploring that further.

You can use Context https://reactjs.org/docs/context.html
const AppContext = React.createContext(null);
const App = () => (
<AppContext.Provider value={...//your component} >
//...
</AppContext.Provider>)
)
const ChildComponent = () => {
const Component = useContext(AppContext);
return (
<div>
//...
{Component}
</div>
)
}

Related

Render only components with changes

I have an array with thousands of strings and is passed to a component:
Main component:
const array = ['name1', 'name2', 'name3'];
const [names, setNames] = useState(array);
const onClick = (index) => {
setNames(names.map((name, i) => {
if (i === index) {
return 'name changed';
}
};
};
return (
<ul>
{array.map((name, index) => (
<li key={index}>
<ShowName name={name} key={index} onClick={() => onClick(index)} />
</li>
)}
</ul>
);
ShowName component:
let a = 0;
export default function ShowName({ name, onClick }) {
a += 1;
console.log(a);
return (
<button type="button" onClick={onClick}>{name}</button>
);
}
There's also a button which changes a name randomly. But whenever the button is pressed, all the ShowName components are rerendering. I've been trying to use useCallback and useMemo, but the components are still rerendering x times (x is the length of the array).
const ShowNameHoc = ({ name }) => {
return <ShowName name={name} />
};
return (
<div>
{array.map((name, index) => <ShowNameHoc name={name} key={index} />)}
</div>
);
What should I do if I only want to rerender the component with a change?
You have a misunderstanding in the concepts here. The default is for React to call render on all children, regardless of whether the props changed or not.
After that happened, React will compare that new Virtual DOM to the current Virtual DOM and then only update those parts of the real DOM that changed.
That's why the code in a render method should be quick to execute.
This behavior can be changed by using features like useMemo, PureComponents or shouldComponentUpdate.
References:
https://reactjs.org/docs/rendering-elements.html (Bottom):
Even though we create an element describing the whole UI tree on every tick, only the text node whose contents have changed gets updated by React DOM.
https://reactjs.org/docs/optimizing-performance.html#avoid-reconciliation
Even though React only updates the changed DOM nodes, re-rendering still takes some time. In many cases it’s not a problem, but if the slowdown is noticeable, you can speed all of this up by overriding the lifecycle function shouldComponentUpdate, which is triggered before the re-rendering process starts.
...
In most cases, instead of writing shouldComponentUpdate() by hand, you can inherit from React.PureComponent. It is equivalent to implementing shouldComponentUpdate() with a shallow comparison of current and previous props and state.
Also, read this for some more background info: https://dev.to/teo_garcia/understanding-rendering-in-react-i5i
Some more detail:
Rendering in the broader sense in React means this (simplified):
Update existing component instances with the new props where feasible (this is where the key for lists is important) or create a new instance.
Calling render / the function representing the component if shouldComponentUpdate returns true
Syncing the changes to the real DOM
This gives you these optimization possibilities:
Ensure you are reusing instances instead of creating new ones, e.g. by using a proper key when rendering lists. Why? New instances always result in the old DOM node to be removed from the real DOM and a new one to be added. Even when unchanged. Reusing an instance will only update the real DOM if necessary. Please note: This has no effect on whether or not render is being called on your component.
Make sure your render method doesn't do heavy lifting or if it does, memoize those results
Use PureComponents or shouldComponentUpdate to prevent the call to render altogether in scenarios where props didn't change
Answering your specific question:
To actually prevent your ShowName component from being rendered - into the Virtual DOM - if their props changed, you need to perform the following changes:
Use React.memo on your ShowName component:
function ShowName({ name, onClick }) {
return (
<button type="button" onClick={onClick}>{name}</button>
);
}
export default memo(ShowName);
Make sure the props are actually unchanged by not passing a new callback to onClick on each render of the parent. onClick={() => onClick(index)} creates a new anonymous function every time the parent is being rendered.
It's a bit tricky to prevent that, because you want to pass the index to this onClick function. A simple solution is to create another component that is passed the onClick with the parameter and the index and then internally uses useCallback to construct a stable callback. This only makes sense though, when rendering your ShowName is an expensive operation.
That is happening because you are not using the key prop on <ShowName/> component.
https://reactjs.org/docs/lists-and-keys.html
it could look something like this
return (
<div>
{array.map(name => <ShowName key={name} name={name} />)}
</div>
);

Pass data to one of the same components

<Comp1 />
<div>
<Comp1 />
<Comp2 />
</div>
I am new to React. I want to pass data from Comp2 to its sibling Comp1 only. I know using a parent component to pass props but in this case I have to rewrite Comp1 to get state from its parent, which will affect all the Comp1. How can I make only chosen Comp1 receive the data and don't bother the else?
There is not a straightforward solution to this, but you do have a couple of options:
Option 1
The most direct way would be as you described - having Comp2 pass data up to its parent using an event listener, then having the parent pass it back down to Comp1. This can be an optional prop being passed to Comp1, so it doesn't matter that your outer Comp1 won't receive that prop.
For example:
import React from 'react';
const Comp1 = ({data='Default Value'}) => (
<p>{data}</p>
)
const Comp2 = ({onData}) => (
<button onClick={e => onData(Math.random())}>Change Value</button>
)
export default function App() {
let [data, setData] = React.useState(null);
return (
<div>
<Comp1/>
<div>
<Comp1 data={data}/>
<Comp2 onData={setData}/>
</div>
</div>
);
}
This is probably your best option, and by the sound of things, it might be good to find a way to refactor your app so that this option becomes more viable. There's usually a way to change your app structure to make this work better.
If you really want siblings to have a more direct line of communication with each other, you could give Comp1 a ref of Comp2, but I wouldn't encourage this.
Option 2
Another option would be to use contexts. This gives anyone the power to communicate with anyone who uses the same context. There is a lot of power in this feature. Some people set up a Redux-like system using contexts and reducers to let any part of the application (or larger component they put the context provider in) communicate with any other part. See this article for more information on using contexts to manage application state.
import React from 'react';
let context = React.createContext()
const Comp1 = () => {
let ctx = React.useContext(context) || {};
return <p>{ctx.data || 'Default Value'}</p>
}
const Comp2 = () => {
let ctx = React.useContext(context);
return <button onClick={e => ctx.setData(Math.random())}>Change Value</button>
}
export default function App() {
let [data, setData] = React.useState();
return (
<div>
<Comp1/>
<div>
<context.Provider value={{data, setData}}>
<Comp1/>
<Comp2/>
</context.Provider>
</div>
</div>
);
}
Option 3
For completeness, A third option would be using something like Redux to help share state. Only use this option if you are already using Redux, or if you really want/need it and understand what you're getting into. Redux is not for every project, everyone does not need it.
Side Note
I realize you said you were new to React. For brevity and for other Googlers, I used a lot of React hooks in my examples (The functions like React.useState, React.useContext, etc). These can take a little bit to understand, and I don't expect you to learn how to use them just to solve your problem. In fact, if you're new to React, I would strongly encourage you to just go with option 1 using the class syntax you've learned how to use already. As you get some more practice and start feeling the limits of the first option, then you can start trying the other things out.
In react, data always moves from top to down, so there is no true way to pass information sibling to sibling without going through some higher structure. You could use context, but again, its provider has to wrap around both sibling components, meaning it has to be implemented in the parent component(App). It is also intended for passing data between deeply nested sibling components to avoid passing props multiple levels deep. In your case where props only have to be passed one level deep, it is best to just store state in the parent component(App).
Here is what context would look like for your App (its more trouble than its worth at this point):
https://codesandbox.io/s/objective-hellman-sdm55?file=/src/App.js
For this use case I would suggest using the useState hook in the parent component and passing down a value & function to the specific child components.
pseudo code:
<Parent>
const [value, setValue] = useState();
<Comp1 onClick={setvalue} />
<Comp2 value={value} />
</Parent>
In my opinion, for your use case, Redux and the Context API are a bit overkill.
You can research about state and props.
References: https://flaviocopes.com/react-state-vs-props

Change handlers in react hooks

For a while a would like to start using react function components with react hooks instead of class extending react component, but there is one thing which discourages me. Here is an example from very first intro of react hooks:
import React, { useState } from 'react'
import Row from './Row'
export default function Greeting(props) {
const [name, setName] = useState('Mary');
function handleNameChange(e) {
setName(e.target.value);
}
return (
<section>
<Row label="Name">
<input
value={name}
onChange={handleNameChange}
/>
</Row>
</section>
)
}
There is a handleNameChange declaration used as a change handler for input. Let's imagine that Greeting component updates really really frequently because of some reason. Does change handle initialize every time on every render? From JavaScript aspect of view how bad is that?
Does change handle initialize every time on every render?
Yes. That's one reason for the useCallback hook.
From JavaScript aspect of view how bad is that?
Fundamentally, it's just creating a new object. The function object and the underlying function code are not the same thing. The underlying code of the function is only parsed once, usually into bytecode or a simple, quick version of compilation. If the function is used often enough, it'll get aggressively compiled.
So creating a new function object each time creates some memory churn, but in modern JavaScript programming we create and release objects all the time so JavaScript engines are highly optimized to handle it when we do.
But using useCallback avoids unnecessarily recreating it (well, sort of, keep reading), by only updating the one we use when its dependencies change. The dependencies you need to list (in the array that's the second argument to useCallback) are things that handleNameChange closes over that can change. In this case, handleNameChange doesn't close over anything that changes. The only thing it closes over is setName, which React guarantees won't change (see the "Note" on useState). It does use the value from the input, but it receives the input via the arguments, it doesn't close over it. So for handleNameChange you can leave the dependencies blank by passing an empty array as the second argument to useCallback. (At some stage, there may be something that detects those dependencies automatically; for now, you declare them.)
The keen-eyed will note that even with useCallback, you're still creating a new function every time (the one you pass in as the first argument to useCallback). But useCallback will return the previous version of it instead if the previous version's dependencies match the new version's dependencies (which they always will in the handleNameChange case, because there aren't any). That means that the function you pass in as the first argument is immediately available for garbage collection. JavaScript engines are particularly efficient at garbage collecting objects (including functions) that are created during a function call (the call to Greeting) but aren't referenced anywhere when that call returns, which is part of why useCallback makes sense. (Contrary to popular belief, objects can and are created on the stack when possible by modern engines.) Also, reusing the same function in the props on the input may allow React to more efficiently render the tree (by minimizing differences).
The useCallback version of that code is:
import React, { useState, useCallback } from 'react' // ***
import Row from './Row'
export default function Greeting(props) {
const [name, setName] = useState('Mary');
const handleNameChange = useCallback(e => { // ***
setName(e.target.value) // ***
}, []) // *** empty dependencies array
return (
<section>
<Row label="Name">
<input
value={name}
onChange={handleNameChange}
/>
</Row>
</section>
)
}
Here's a similar example, but it also includes a second callback (incrementTicks) that does use something it closes over (ticks). Note when handleNameChange and incrementTicks actually change (which is flagged up by the code):
const { useState, useCallback } = React;
let lastNameChange = null;
let lastIncrementTicks = null;
function Greeting(props) {
const [name, setName] = useState(props.name || "");
const [ticks, setTicks] = useState(props.ticks || 0);
const handleNameChange = useCallback(e => {
setName(e.target.value)
}, []); // <=== No dependencies
if (lastNameChange !== handleNameChange) {
console.log(`handleNameChange ${lastNameChange === null ? "" : "re"}created`);
lastNameChange = handleNameChange;
}
const incrementTicks = useCallback(e => {
setTicks(ticks + 1);
}, [ticks]); // <=== Note the dependency on `ticks`
if (lastIncrementTicks !== incrementTicks) {
console.log(`incrementTicks ${lastIncrementTicks === null ? "" : "re"}created`);
lastIncrementTicks = incrementTicks;
}
return (
<div>
<div>
<label>
Name: <input value={name} onChange={handleNameChange} />
</label>
</div>
<div>
<label>
Ticks: {ticks} <button onClick={incrementTicks}>+</button>
</label>
</div>
</div>
)
}
ReactDOM.render(
<Greeting name="Mary Somerville" ticks={1} />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
When you run that, you see that handleNameChange and incrementTicks were both created. Now, change the name. Notice that nothing is recreated (well, okay, the new ones aren't used and are immediately GC'able). Now click the [+] button next to ticks. Notice that incrementTicks is recreated (because the ticks it closes over was stale, so useCallback returned the new function we created), but handleNameChange is still the same.
Looking strictly from a JavaScript perspective (ignoring React), defining a function inside a loop (or inside another function that's called regularly) is unlikely to become a performance bottleneck.
Take a look at these jsperf cases. When I run this test, the function declaration case runs at 797,792,833 ops/second. It's not necessarily a best practice either, but it's often a pattern that falls victim to premature optimization from programmers who assume that defining a function must be slow.
Now, from React's perspective. It can become a challenge for performance is when you are passing that function to child components who end up re-rendering because it's technically a new function each time. In that case it becomes sensible to reach for useCallback to preserve the identity of the function across multiple renders.
It's also worth mentioning that even with the useCallback hook, the function expression is still redeclared with each render, it's just that it's value is ignored unless the dependency array changes.

Function inside functional component in React hooks - Performance

Need suggestion on having function within a functional component in react Hooks.
As far as I researched, many are saying it is bad practice
because it creates nested/inner function every time we call re-render.
After doing some analysis,
I found we can use onClick={handleClick.bind(null, props)} on the element and place the function outside the functional component.
Example:
const HelloWorld = () => {
function handleClick = (event) => {
console.log(event.target.value);
}
return() {
<>
<input type="text" onChange={handleClick}/>
</>
}
}
Please advise if there is any alternative way.
Thanks in advance.
Don't worry about it
Don't worry about creating new functions on each render. Only in edge cases does that impede your performance.
Setting onClick handlers are not one of those, so just create a new function on each render.
However, when you need to make sure you use the same function every time, you can use useCallback
Why not use useCallback for onClick
Here is a reason why you shouldn't bother with useCallback for onClick handlers (and most other event handlers).
Consider the following code snippets, one without useCallback:
function Comp(props) {
return <button onClick={() => console.log("clicked", props.foo)}>Text</Button>
}
and one with useCallback:
function Comp(props) {
const onClick = useCallback(() => {
console.log("clicked", props.foo)
}, [props.foo])
return <button onClick={onClick}>Text</Button>
}
The only difference in the latter is that React doen's have
to change the onClick on your button if props.foo remains the same.
Changing the callback is a very cheap operation, and it's not at all
worth complicating your code for the theoretical performance improvement it gives.
Also, it's worth noting that a new function is still created on every render
even when you use useCallback, but useCallback will return the old one
as long as the dependencies passed as the second argument are unchanged.
Why ever use useCallback
The point of using useCallback is that if you compare two functions with reference
equality, fn === fn2 is true only if fn and fn2 point to the same function in memory.
It doesn't matter if the functions do the same.
Thus, if you have memoisation or otherwise only run code when the function changes,
it can be useful to use useCallback to use the same function again.
As an example, React hooks compare old and new dependencies, probably using Object.is.
Another example is React.PureComponent, which will only re-render when props or state have changed. This can be useful for components that use a lot of resources to render. Passing e.g. a new onClick to a PureComponent on each render will cause it to re-render every time.
many are saying it is bad practice because it creates nested/inner function every time we call re-render
No, inner functions / closures are so common, there is no problem with them. The engine can heavily optimize those.
The point here is that you pass the function as a prop to the child component. And as the function was "recreated", it does not equal the previous function passed, annd thus the child does rerender (and that's whats bad for performance).
You can resolve that with useCallback, which memoizes the function reference.
Interesting question, me and my coleagues had some worries about this, so I did a test.
I have created 1 Component with Hooks and 1 Component with Class, put some functions there and then render it 1000x times.
The Component with Class looks like this:
export class ComponentClass extends React.PureComponent {
click1 = () => {
return console.log("just a log");
};
render() {
return (
<>
<span onClick={this.click1}>1</span>
</>
);
}
}
The Component with Hooks looks like this:
export const ComponentHook = React.memo((props) => {
const click1 = () => {
return console.log("just a log");
};
return (
<>
<span onClick={click1}>1</span>
</>
);
});
I have added more click handlers to the components and then rendered them some 1000s times, the Class is faster as it does not define the functions each render, if you increase the number of functions defined, then the difference will be bigger:
Here it is a codesandbox so you can test the performance Class vs Hooks : https://codesandbox.io/s/hooks-vs-class-forked-erdpb
useCallback
You can use useCallback feature :
const HelloWorld = ({ dispatch }) => {
const handleClick = useCallback((event) => {
dispatch(() => {console.log(event.target.value)});
})
return() {
<>
<input type="name" onChange={handleClick}/>
</>
}
}
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).
For further details visit react documentation reference: React useCallback
Old Solution
First solution:
To pass the your handleClick function to your functional component.
const HelloWorld = (props) => {
return() {
<>
<input type="name" onChange={props.handleClick}/>
</>
}
}
Second solution:
To define your function outside of your functional component.
Inspired by #tibbus 's benchmark, I made this one that tests the perfomance using or not the useCallback hook. After several executions, it seams that the use of useCallback can be very important for high frequency rendering.
Execution 1
Execution 2
Execution 3
https://codesandbox.io/s/usecallback-vs-raw-definition-xke9v?file=/src/App.js
As per React Documentation (ending part),
The problem with latter 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.
Class field syntax:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
arrow function in the callback syntax:
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>
);
}
}
I would honestly just use a class component in these cases. I'm aware of premature optimization, but creating a new function each time does just seem like extravagant wastefulness without much of a maintainability upside. tibbus has demonstrated the perf hit, and inline functions are arguably less readable than class methods. All you're losing out is the slick feeling of writing a functional component.
Just useCallback
Why would you need to define a function inside a component and not anywhere else? Because you either have to pass it to another child compononent, or you have to use it in an effect, memo, or another callback. For any of those cases if you dont wrap your function in useCallback you will be passing a new function and causing the component to rerender, the memo to re-run, the effect to re-run, or the callback to re-define.
You can never avoid the performance hit of redefining the function itself, but you can avoid the performance hit of performing any computation that has that function as a dependency to know if it has to run or not (be it a component or hook).
So... just wrap every function in your component in useCallback and forget about it, never seen a single in case in which this would cause any harm. If you can define the function outside the component, thats always better.

Is it an anti-pattern to define a function component inside the render() function?

I want to know if it's an anti-pattern or if it affects the component somehow to do something like this:
render() {
const MyFuncComponent = ({ prop1, prop2 }) => (
// code here
)
return (
<div>
<MyFuncComponent prop1={something} prop2={else} />
</div>
)
}
Yes, this is an anti-pattern for the same reason we shouldn't use a Higher-Order-Component inside of render.
Don’t Use HOCs Inside the render Method
React’s diffing algorithm (called reconciliation) uses component identity to determine whether it should update the existing subtree or throw it away and mount a new one. If the component returned from render is identical (===) to the component from the previous render, React recursively updates the subtree by diffing it with the new one. If they’re not equal, the previous subtree is unmounted completely.
Normally, you shouldn’t need to think about this. But it matters for HOCs because it means you can’t apply a HOC to a component within the render method of a component:
render() {
// A new version of EnhancedComponent is created on every render
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// That causes the entire subtree to unmount/remount each time!
return <EnhancedComponent />;
}
The problem here isn’t just about performance — remounting a component causes the state of that component and all of its children to be lost.
This means that the new component will appear in the React tree (which can be explored with the react-devtools) but it won't retain any state and the lifecycle methods like componentDidMount, componentWillUnmount, useEffect will always get called each render cycle.
Solutions
Since there are probably reasons for dynamically creating a component, here are some common patterns that avoid the pitfalls.
Define the new component outside
Either in its own file, or directly above the parent component's definition. Pass any variable as props instead of using the parent component's scope to access the values.
const MyFuncComponent = ({ prop1, prop2 }) => <>{/* code here */}</>;
const MyComponent = props => (
<div>
{props.list.map(({ something, thing }) => (
<MyFuncComponent prop1={something} prop2={thing} />
))}
</div>
);
Helper function
A regular function that returns JSX can be defined and used directly inside another component. It won't appear as a new component inside React's tree, only its result will appear, as if it was inlined.
That way, we can also use variables from the enclosing scope (like props.itemClass in the following example) in addition to any other parameters.
const MyComponent = props => {
// Looks like a component, but only serves as a function.
const renderItem = ({ prop1, prop2 }) => (
<li className={props.itemClass}> {/* <-- param from enclosing scope */}
{prop1} {prop2} {/* other code */}
</li>
);
return <ul>{props.list.map(renderItem)}</ul>;
};
It could also be defined outside the component since it's really flexible.
const renderItem = (itemClass, { prop1, prop2 }) => (
<li className={itemClass}>
{prop1} {prop2} {/* other code */}
</li>
);
const MyComponent = props => (
<ul>
{props.list.map(item => renderItem(props.itemClass, item))}
</ul>
);
But at that point, we should just define a React component instead of faking it with a function. Use React in a predictable manner and to its full potential.
Inline the logic
It's really common to inline JSX inside of a condition or a map callback.
const MyComponent = (props) => (
<ul>
{props.list.map(({ something, thing }) => (
<li className={props.itemClass}>
{something} {thing} {/* other code */}
</li>
))}
</ul>
);
If we find ourselves copy-pasting this same inlined JSX everywhere, it might be time to wrap it up in its own reusable component.
I think in general people avoid defining functions in render but according to this blog post it is not neccesarily a bad practice. The blog post focuses on inline event handler functions being defined in render but I would guess it applies to any function defined in render. Defining functions in render means there is the overhead of redfining them each time render is called but that may not make a noticible performance difference depending on your component.
For the particular example you gave I would reccomend not to define another react component in render. If you do define any functions in render they should be cohesive to what render is doing. Defining another component or adding a bunch of functions inside of render can make in unwieldy and hard to understand what the code is doing.

Categories

Resources