React does not have computed properties out of the box like others frameworks do (Ember, Vue, etc.) so, how can we achieve this behaviour the right way ? The most common practice is to "compute" things in the render method if is a class component or just in the body if is a function component.
The computation is beeing done on every render, meaning that is generating a new variable even if the values that need to be computed didn't change. Plus if we are passing this as a prop to a child component and is not a primitive type (string, number or boolean) the child will re-render thinking that is a new value and this can cause performance issues.
Extract Computation to a function and use memoization to prevent from beeing call if the arguments didn't change, this will give us performance gains if we are doing expensive computations like big lists.
Class component
import React from 'react'
import memoize from 'lodash/memoize'
import MenuItem from './MenuItem'
class RenderSuggestion extends React.Component {
renderSuggestionStyles = (suggestionValue) => ({ pointerEvents: suggestionValue ? 'all' : 'none' })
renderSuggestionStylesMemo = memoize(this.renderSuggestionStyles)
render(){
const { suggestion, ...other } = this.props
return (
<MenuItem
{...other}
style={this.renderSuggestionStylesMemo(suggestion.value + 1)}
/>
)
}
}
Function component
import React from 'react'
import memoize from 'lodash/memoize'
import MenuItem from './MenuItem'
const renderSuggestionStyles = suggestionValue => ({ pointerEvents: suggestionValue ? 'all' : 'none' })
const renderSuggestionStylesMemo = memoize(renderSuggestionStyles)
function RenderSuggestion({ suggestion, ...other }) {
return (
<MenuItem
{...other}
style={renderSuggestionStylesMemo(suggestion.value + 1)}
/>
)
}
Here if we wouldn't use a computed property the right way (without memoization) the child component (MenuItem) would re-render unnecessary when props change.
Another cool way of fixing this problem is using reselect
import React from 'react'
import { createSelector } from "reselect";
import MenuItem from './MenuItem'
class RenderSuggestion extends React.Component {
renderSuggestionStyles = createSelector(
(suggestionValue) => ({ pointerEvents: suggestionValue ? 'all' : 'none' })
)
render(){
const { suggestion, ...other } = this.props
return (
<MenuItem
{...other}
style={this.renderSuggestionStyles(suggestion.value + 1)}
/>
)
}
}