Will using functions to render content hurt react performance? - reactjs

This article (and other places) mention now calling functions inside of render:
https://levelup.gitconnected.com/optimize-react-performance-c1a491ed9c36?ref=reddit
I've always used a pattern for large React class components where I'll move some of the JSX out of the render function into methods. This avoids having a billion little one-time-use separate components and it also avoids having to put lengthy if/then or ternary logic inside of a render area which I find slightly harder to read.
class HelpModal extends React.Component {
state = { visible: false };
renderContent = () => {
if (this) {
return <div />
}
if (that) {
return <span />
}
}
render() {
return (
<Modal visible={this.state.visible}>
{this.renderContent()}
</Modal>
);
}
}
I've seen this strategy in a bunch of places, but now it sounds like, based on the above blog post, that this may be a bad practice performance-wise?

It depends on how you use inline functions.
React calls the render function after props and/or state changes. Each time they change the render function is called.
If you compute things that haven't changed because of the new props/state values then this inline function has indeed a negative impact on your app's performance.
Example:
render() {
<div>
{ this.props.items.map(() => <SomeComponent />) }
</div>
}
If you compute it here or anywhere else doesn't change the fact that you need to compute it each time the render function is called. No negative effect at all.
computeStaticStuff(x, y) {
return x + y > 0 ? <p /> : <i />;
}
render() {
<div>
{ () => this.computeStaticStuff(5, 6) }
</div>
}
This would be a ((n) extremely stupid) example of recomputation that is not needed at all.

Related

Is it safe to pass a component props called "children"?

Can I pass children={someArray} as props to a component without muddling the namespace for props.children? I've tried it, and I'm actually surprised it doesn't seem to break anything. Still, it seems like a very bad idea. Should I be concerned about potential collisions down the road, or does React protect against this somehow?
For context, I'm writing a HOC for creating an accordion-style row in a list, and I want to keep the props it passes down as simple and generic as possible, for the sake of reuse. The parent/children paradigm just seems the most intuitive.
Here's a simplified version:
function ProductList(props) {
return (
<List>
{props.products.map(product => (
<ProductWithVarieties parent={product} children={product.varieties} key={item.id} />
))}
</List>
)
}
function withChildren(ParentComponent, ChildComponent) {
return class extends React.Component {
// OMMITTED: a constructor, some life cycle hooks, event handlers, etc.
render(props) {
const renderChildren = () => {
return this.props.children.map(child => {
return <ChildComponent {...child} key={child.id}/>
})
}
return (
<div>
<ParentComponent
{...this.props.parent}
onClick={this.toggleChildren}
hasChildren={this.props.children.length > 0} />
{ this.state.showChildren ? renderChildren() : null }
</div>
)
}
}
}
const ProductWithVarieties = withChildren(ProductComponent, VarietyComponent)
It is "safe" to pass it as children prop, but from standardswise you shouldn't do it and rather call it something different

ReactJS Functional Component vs Component Functions for conditional rendering

Sometimes I want to move my conditional rendering out of render(), and I always have dilemma between these two approaches:
class MyComponent extends React.Component {
_renderSomething() => {
const {x, y, z} = this.props
// conditional rendering based on props
}
render() {
return (
{ this._renderSomething }
// vs
{ renderSomething(this.props) }
// which one is better?
)
}
}
const renderSomething = (props) => {
const {x, y, z} = props
// conditional rendering based on props
}
export default MyComponent
Any performance difference between _renderSomething and this.renderSomething?
When should I use which?
There is a performance penalty with Functional Components vs Functions returning elements. An example would be this -
// Component approach
let Tab = ({label, link}) => <li><a href={link}>{label}</a></li>;
class Tabs extends Component {
render(){
return (
// notice the key prop is handled by parent render
<ul>{this.props.tabs.map(tab => <Tab {...tab} key={link}>)}</ul>
)
}
}
// function based approach, notice the key prop is handled by the function
let tab = ({label, link}) => <li key={link}><a href={link}>{label}</a></li>;
class Tabs extends Component {
render(){
return (
<ul>{this.props.tabs.map(item => tab(item))}</ul>
)
}
}
In Component example, you will end up with unnecessary intermediate Tab components, which will add to the virtual dom, however small they are. And then as they grow, these components will eventually cause slow renders. React will need to keep track of these components over subsequent renders. And these being functional components, you would not have access to shouldComponentUpdate based optimizations.
The function version will not suffer from this as it returns Elements directly, instead of components. Also, with smaller functions, there will be gains due to code inlining.
An extended discussion into this approach is here.
in general, a stateless component should probably be a pure function that just returns the component to render
Below, you could replace <EmptyList /> with <ul></ul> but the idea is you can abstract wherever you feel is necessary – in this case, I thought it would be nice to have some placeholder text for empty lists, so making it a stateless functional component was an easy choice
So little of your app actually needs state – in fact, you should be striving to remove it wherever possible. When nothing needs state, everything can be expressed with functions
const EmptyList = ({ placeholder = "There are no items to display" }) =>
<ul><li>{ placeholder }</li></ul>
const ListItem = ({ text = "" }) =>
<li>{ text }</li>
const List = ({ items = [] }) =>
items.length === 0
? <EmptyList />
: <ul>{ items.map (text => <ListItem text={text} />) }</ul>
ReactDOM.render(<List items={[1,2,3]} />, document.querySelector('#list1'))
ReactDOM.render(<List items={[]} />, document.querySelector('#list2'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="list1"></div>
<div id="list2"></div>

Which react declaration to use

I am new to react and seen following declaration in few tutorials.
I am confuse on which to use. Is there any guideline on which to prefer under difference situation?
Declaration 1
const elem = function() {
return <div>Hello World!</div>
}
Declaration 2
const elem2 = <div>Hello World!</div>
Declaration 3
class App extends Component {
render() {
return (
<div>Hello World!</div>
);
}
}
Considering
class App extends Component {
render() {
return (
<div>Hello World!</div>
);
}
}
is a React component and hence should be used when you wish to create separate Components for different functionalities for you Application which includes states and props being passed.
As per
const elem2 = <div>Hello World!</div>
it should be used when the JSX elements do not contain extra logic and contian static contents
const elem = function() {
return <div>Hello World!</div>
}
should ideally be used when you want to perform certain modification on the data and return the JSX element, also if you wish to perform some conditional returns and stateless retuns
You can always use functional components when the component is composed only from the render function.
That means, if your component has the following form:
class MyComponent extends React.Component {
render() {
... do something ...
}
}
then you can replace it with
const MyComponent = (props, context) => {
... do something ...
}
Functional components cannot have state (they are stateless) and you cannot access the component lifecycle (e.g. componentDidMount). You also cannot use pure rendering with them because you cannot override shouldComponentUpdate.
They are the purest form of a component, they convert properties into UI and have no other side-effects.
I would recommend to use them as much as possible because they enforce a good programming style (especially with redux architecture) but you won't be able to use them everytime. The "smarter" a component is, the less possible it will be to use a functional component (see Smart vs Dumb components).
They are especially useful when defining Higher Order Components.
Also note that you can often combine all approaches, consider
render() {
const renderContents = () => {
return (
<div className="contents">
this.props.children
</div>
);
}
return (
<div>
{renderContents}
</div>
);
}
You can define a "component" inside other component's render. As you can see, there is no difference between a function and a functional component. And the same applies for constants. Defining a constant <div /> is not different from defining a constant string or a number. You don't need to wrap it into a component/function. You can also do things like this:
const contents = (() => {
if (props.children.count === 0) {
return null;
}
return (
<div>{props.children}</div>
);
})();
This is an immediately invoked function.

React passing parameter with arrow function in child component

I have these parent and child component, I want to pass click function to select an item in child component. Yet it seems the function in child component become automatically called instead of waiting until the user click the element. To make it clearer here is my parent and child components
export class ParentView extends Component {
state = {
selectedItem: {}
}
handleClick = (item) => {
alert('you click me');
this.setState({selectedItem: item});
}
render() {
let item = { name: 'Item-1' };
return (
<div>
<ChildItem item={item} handleClick={this.handleClick} />
</div>
);
}
}
export class ChildItem extends Component {
render() {
const {item, handleClick} = this.props;
return (
<div>
<a onClick={handleClick(item)} />
</div>
);
}
}
Those are my components using arrow function to pass handleClick to child component, yet alert always being called at first render without being triggered by user. Any suggestion?
You should pass a function itself to onClick, not a result of the passed function invocation.
If you would like to invoke it with param, you have options:
bind it with item with handleClick.bind(this, item). bind creates a new function will have a predefined first parameter - item
pass new arrow function like () => handleClick(item)
An example below:
export class ChildItem extends Component {
render() {
const { item, handleClick } = this.props;
return (
<div>
<a onClick={() => handleClick(item)} />
</div>
)
}
}
In your code you're invoking a function in onClick declaration, so the result of handleClick execution will be passed to onClick, what is most likely not something you wanted to achieve.
<a onClick={handleClick(item)} />
Update:
as #dhilt wrote, there is a drawback of such approach. Since the newly created arrow function and .bind also creates new function every time the render method of ChildItem is invoked, react will threat the resulted react element as a different, comparing to the previous "cached" result of render method, that means that likely it might lead to some performance problems in the future, there is even a rule regarding this problem for eslint, but you shouldn't just follow this rule because of two points.
1) performance problems should be measured. we don't forbid using Array.prototype.forEach in favor of a regular for because for is the same or "faster".
2) definition of click handlers as class properties leads to increasing of the initializing step of the component instance. Re-render is fast and efficient in react, so sometimes the initial rendering is more important.
Just use what's better for you and likely read articles like this https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578
Accepted answer has a performance hit: ChildItem component will be re-rendered even if data hasn’t changed because each render allocates a new function (it is so because of .bind; same with arrow functions). In this particular case it is very easy to avoid such a problem by getting handler and its argument right from the props on new public class field:
export class ChildItem extends Component {
onClick = () => {
this.props.handleClick(this.props.item);
}
render() {
return (
<div>
<a onClick={this.onClick} />
</div>
);
}
}
ParentView remains untouched.
The ES6 way:
Using arrow functions =>
onClick={() => handleClick(item)}
(#havenchyk's answer is the ES5 way).

ReactJS get rendered component height

I'm attempting to integrate or create a React version of https://github.com/kumailht/gridforms, to do so I need to normalize the height of the columns inside of the row. The original takes the height of the grid row and applies it to the children columns.
I had planned to get the height of the row and then map it to a property of the child, though from my attempts I'm thinking this might not be the ideal way or even possible?
Below is my current code.
GridRow = React.createClass({
render(){
const children = _.map(this.props.children, child => {
child.props.height = // somehow get row component height
return child
})
return (<div data-row-span={this.props.span} {...this.props}>
{children}
</div>)
}
})
GridCol = React.createClass({
render(){
return (<div data-field-span={this.props.span} style={{height:this.props.height}} {...this.props}>
{this.props.children}
</div>)
}
})
I tested setting the style this way and it will work, however getting the height isn't.
EDIT: Fiddle:
https://jsfiddle.net/4wm5bffn/2/
A bit late with the answer but technically you can get element hight this way:
var node = ReactDOM.findDOMNode(this.refs[ref-name]);
if (node){
var calculatedHeight = node.clientHeight;
}
According to current React docs, the preferred use of refs is to pass it a callback rather than a string to be accessed elsewhere in this.refs.
So to get the height of a div (within a React.Component class):
componentDidMount() {
this.setState({ elementHeight: this.divRef.clientHeight });
}
render() {
return <div ref={element => this.divRef = element}></div>
}
Or it works this way, though I don't know if this is advisable since we set state in the render method.
getHeight(element) {
if (element && !this.state.elementHeight) { // need to check that we haven't already set the height or we'll create an infinite render loop
this.setState({ elementHeight: element.clientHeight });
}
}
render() {
return <div ref={this.getHeight}></div>;
}
Reference: https://facebook.github.io/react/docs/more-about-refs.html
Don't know about anyone else but I always have to get it on the next tick to be sure of getting the correct height and width. Feels hacky but guessing it's to do with render cycle but I'll take it for now. onLayout may work better in certain use cases.
componentDidMount() {
setTimeout(() => {
let ref = this.refs.Container
console.log(ref.clientHeight)
console.log(ref.clientWidth)
}, 1)
}
Here is an example of using refs and clientWidth/clientHeight:
import React, { Component } from 'react';
import MyImageSrc from './../some-random-image.jpg'
class MyRandomImage extends Component {
componentDidMount(){
let { clientHeight, clientWidth } = this.refs.myImgContainer;
console.log(clientHeight, clientWidth);
}
render() {
return (
<div ref="myImgContainer">
<img src={MyImageSrc} alt="MyClickable" />
</div>
);
}
}
export default MyRandomImage;
Note: this appears to work for width reliably, but not height. Will edit if I find a fix...
My personal opinion is to try and avoid using static and measured sizes like this if you can avoid it because it can complicate the application unnecessarily. But sometimes you cannot get around it. Your component will need to be mounted before you can get a size from it.
General approach:
Give the element a ref
When the element is rendered, grab the ref and call .clientHeight and/or .clientWidth
Put the values on the state or pass with props
Render the element that needs the size from the state variables
In your case you want to grab the size of a column you can do something like:
GridRow = React.createClass({
render(){
const children = _.map(this.props.children, child => {
child.props.height = // somehow get row component height
return child
})
return (<div data-row-span={this.props.span} {...this.props}>
<GridCol onSizeChange={(size) => {
//Set it to state or whatever
console.log("sizeOfCol", size);
}} />
</div>)
}
})
GridCol = React.createClass({
componentDidMount(){
//Set stizes to the local state
this.setState({
colH: this.col.clientHeight,
colW: this.col.clientWidth
});
//Use a callback on the props to give parent the data
this.props.onSizeChange({colH: this.col.clientHeight, colW: this.col.clientWidth})
}
render(){
//Here you save a ref (col) on the class
return (<div ref={(col) => {this.col = col}} data-field-span={this.props.span} style={{height:this.props.height}} {...this.props}>
<.... >
</div>)
}
})
According this answer sizes of a component can be turned out having zero width or height inside componentDidMount event handler. So I'm seeing some ways to solve it.
Handle the event on top-level React component, and either recalculate the sizes there, or redraw the specific child component.
Set the load event handler on the componentDidMount to handle loading the cells into the react component to recalculate the proper sizes:
componentDidMount = () => {
this.$carousel = $(this.carousel)
window.addEventListener('load', this.componentLoaded)
}
Then in the componentLoaded method just do what you need to do.
A bit more late, but I have an approach which can be used without using the getElementById method. A class based component could be created and the sample code can be used.
constructor(props) {
super(props);
this.imageRef = React.createRef();
}
componentDidMount(){
this.imageRef.current.addEventListener("load", this.setSpans);
}
setSpans = () => {
//Here you get your image's height
console.log(this.imageRef.current.clientHeight);
};
render() {
const { description, urls } = this.props.image;
return (
<div>
<img ref={this.imageRef} alt={description} src={urls.regular} />
</div>
);
}
Above solutions are good. I thought I'd add my own that helped me solve this issue + others discussed in this question.
Since as others have said a timeout function is unpredictable and inline css with javascript variable dependencies (ex. style={{height: `calc(100vh - ${this.props.navHeight}px)`}}) can alter the height of elements after the componentDidMount method, there must be an update after all of the elements and inline javascript-computed css is executed.
I wasn't able to find very good information on which elements accept the onLoad attribute in React, but I knew the img element did. So I simply loaded a hidden image element at the bottom of my react component. I used the onLoad to update the heights of referenced components elsewhere to yield the correct results. I hope this helps someone else.
_setsectionheights = () => {
this.setState({
sectionHeights: [
this.first.clientHeight,
this.second.clientHeight,
this.third.clientHeight,
]
});
}
render() {
return (
<>
<section
ref={ (elem) => { this.first = elem } }
style={{height: `calc(100vh - ${this.props.navHeight}px)`}}
>
...
</section>
...
<img style={{display: "none"}} src={..} onLoad={this._setsectionheights}/>
</>
);
}
For the sake of being thorough, the issue is that when the componentDidMount method is executed, it only considers external css (speculation here). Therefore, my section elements (which are set to min-height: 400px in external css) each returned 400 when referenced with the clientHeight value. The img simply updates the section heights in the state once everything before it has loaded.
I'd rather do it in componentDidUpdate, but by making sure a condition is met to prevent an infinite loop:
componentDidUpdate(prevProps, prevState) {
const row = document.getElementById('yourId');
const height = row.clientHeight;
if (this.state.height !== height) {
this.setState({ height });
}
}

Resources