I'm running lint with my React app, and I receive this error:
error JSX props should not use arrow functions react/jsx-no-bind
And this is where I'm running the arrow function (inside onClick):
{this.state.photos.map(tile => (
<span key={tile.img}>
<Checkbox
defaultChecked={tile.checked}
onCheck={() => this.selectPicture(tile)}
style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
/>
<GridTile
title={tile.title}
subtitle={<span>by <b>{tile.author}</b></span>}
actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
>
<img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
</GridTile>
</span>
))}
Is this a bad practice that should be avoided? And what's the best way to do it?
Why you shouldn't use inline arrow functions in JSX props
Using arrow functions or binding in JSX is a bad practice that hurts performance, because the function is recreated on each render.
Whenever a function is created, the previous function is garbage collected. Rerendering many elements might create jank in animations.
Using an inline arrow function will cause PureComponents, and components that use shallowCompare in the shouldComponentUpdate method to rerender anyway. Since the arrow function prop is recreated each time, the shallow compare will identify it as a change to a prop, and the component will rerender.
As you can see in the following 2 examples - when we use inline arrow function, the <Button> component is rerendered each time (the console shows the 'render button' text).
Example 1 - PureComponent without inline handler
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
onClick = () => this.setState((prevState) => ({
counter: prevState.counter + 1
}));
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ this.onClick } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Example 2 - PureComponent with inline handler
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ () => this.setState((prevState) => ({
counter: prevState.counter + 1
})) } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Binding methods to this without inlining arrow functions
Binding the method manually in the constructor:
class Button extends React.Component {
constructor(props, context) {
super(props, context);
this.cb = this.cb.bind(this);
}
cb() {
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Binding a method using the proposal-class-fields with an arrow function. As this is a stage 3 proposal, you'll need to add the Stage 3 preset or the Class properties transform to your babel configuration.
class Button extends React.Component {
cb = () => { // the class property is initialized with an arrow function that binds this to the class
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Function Components with inner callbacks
When we create an inner function (event handler for example) inside a function component, the function will be recreated every time the component is rendered. If the function is passed as props (or via context) to a child component (Button in this case), that child will re-render as well.
Example 1 - Function Component with an inner callback:
const { memo, useState } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
To solve this problem, we can wrap the callback with the useCallback() hook, and set the dependencies to an empty array.
Note: the useState generated function accepts an updater function, that provides the current state. In this way, we don't need to set the current state a dependency of useCallback.
Example 2 - Function Component with an inner callback wrapped with useCallback:
const { memo, useState, useCallback } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter(counter => counter + 1), []);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Using inline functions like this is perfectly fine. The linting rule is outdated.
This rule is from a time when arrow functions were not as common and people used .bind(this), which used to be slow. The performance issue has been fixed in Chrome 49.
Do pay attention that you do not pass inline functions as props to a child component.
Ryan Florence, the author of React Router, has written a great piece about this:
https://reacttraining.com/blog/react-inline-functions-and-performance/
This is because an arrow function apparently will create a new instance of the function on each render if used in a JSX property. This might create a huge strain on the garbage collector and will also hinder the browser from optimizing any "hot paths" since functions will be thrown away instead of reused.
You can see the whole explanation and some more info at https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
Why shouldn't JSX props use arrow functions or bind?
Mostly, because inline functions can break memoization of optimized components:
Traditionally, performance concerns around inline functions in React have been related to how passing new callbacks on each render breaks shouldComponentUpdate optimizations in child components. (docs)
It is less about additional function creation cost:
Performance issues with Function.prototype.bind got fixed here and arrow functions are either a native thing or are transpiled by babel to plain functions; in both cases we can assume it’s not slow. (React Training)
I believe people claiming function creation is expensive have always been misinformed (React team never said this). (Tweet)
When is the react/jsx-no-bind rule useful?
You want to ensure, that memoized components work as intended:
React.memo (for function components)
PureComponent or custom shouldComponentUpdate (for class components)
By obeying to this rule, stable function object references are passed. So above components can optimize performance by preventing re-renders, when previous props have not changed.
How to solve the ESLint error?
Classes: Define the handler as method, or class property for this binding.
Hooks: Use useCallback.
Middleground
In many cases, inline functions are very convenient to use and absolutely fine in terms of performance requirements. Unfortunately, this rule cannot be limited to only memoized component types. If you still want to use it across-the-board, you could e.g. disable it for simple DOM nodes:
rules: {
"react/jsx-no-bind": [ "error", { "ignoreDOMComponents": true } ],
}
const Comp = () => <span onClick={() => console.log("Hello!")} />; // no warning
To avoid creating new functions with the same arguments, you could memoize the function bind result, here is a simple utility named memobind to do it: https://github.com/supnate/memobind
You can remove this error by wrapping the function inside useCallback.
For those wondering when you need to pass data in the callback. Ex.:
const list = todos.map((todo, index) => (
<Todo
onClick={() => { onTodoClick(todo.id, todo.title, index) }
/>
));
Solution
According to the official documentation, you should do:
Move the function arguments into the children component:
const list = todos.map((todo, index) => (
<Todo
onClick={onTodoClick}
todoId={todo.id}
todoTitle={todo.title}
indexOnList={index}
/>
));
In the children component (<Todo />), pass the arguments in the call:
function Todo(props) {
// component properties
const { onClick, todoId, todoTitle, indexOnList } = props;
// we move the call from the parent to the children
const handleClick = useCallback(() => {
onClick(todoId, todoTitle, indexOnList);
}, [todoId, todoTitle, indexOnList]);
return (
<div onClick={handleClick}>
{/* the rest of the component remains the same */}
</div>
);
}
Is this the best solution?
I dislike this solution. You end up with parent's data and logic in the children component. This makes the children component dependent on the parent component, breaking the dependency rule.
That's a big no-no for me.
What I do is just disable this rule. According to Ryan Florence (React Router author), this is not a big deal anyway:
https://medium.com/#ryanflorence/react-inline-functions-and-performance-bdff784f5578
The new (in beta, jan 2023) React tutorial uses both functions and arrow functions as JSX props. This hints strongly at this not being a major concern.
You can use arrow functions using react-cached-handler library, no need to be worried about re-rendering performance :
Note : Internally it caches your arrow functions by the specified key,
no need to be worried about re-rendering!
render() {
return (
<div>
{this.props.photos.map((photo) => (
<Photo
key={photo.url}
onClick={this.handler(photo.url, (url) => {
console.log(url);
})}
/>
))}
</div>
);
}
Other features:
Named handlers
Handle events by arrow functions
Access to the key, custom arguments and the original event
Component rendering performace
Custom context for handlers
You may also see this this error if the function you are using in your onClick handler is a regular (non-inline) function defined outside of the render method but using the function keyword.
Declare your handler function as an arrow function using const, outside of you render method, and React will stop complaining...
Related
I have a component, something like,
const ComponentA = ({ heading })=> {
return (<h1>{ heading }</h>);
};
Is there any difference in rendering this component using below two options,
Option 1
const ComponentB = ()=> {
return ComponentA({ heading: 'Heading test' });
};
Option 2
const ComponentB = ()=> {
return <ComponentA heading='Heading test' />;
};
Yes. There is a very important difference. In option 1, ComponentA is not actually a component as far as React is concerned. In option 2 it is.
The best way to illustrate the difference is to put state or another hook inside of ComponentA. Option 1 will not work as expected. (Note: if it does happen to work as expected, you still shouldn't trust it. This is where bugs can sneak in because they don't cause issues until later).
Here is an example where using hooks appears to work, but breaks after you get the counter past 5. This is because React is treating the hooks inside ComponentA as if they belong to Example. Notice how the JSX version works as expected even after it disappears.
const {useState, useEffect} = React;
const ComponentA = ({ id, heading })=> {
React.useEffect(() => {
console.log(id, 'mounted');
}, []);
return (
<h1>
{ heading }
</h1>
);
};
const Example = () => {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Re-render me</button> {count}
{count < 5 ? ComponentA({ heading: 'Heading test1', id: 1 }) : null}
{count < 3 ? <ComponentA heading='Heading test2' id={2} /> : null}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The reason is JSX (< /> syntax) is actually just a syntax for calling React.createElement. If this is not called, the function component does not get it's own lifecycle, etc. You've bypassed the way React tracks components.
How does this code work ? How can a function be called inside a
component?
import React from 'react'
const ThemeContext = React.createContext('blue');
const App = () =>
<ThemeContext.Provider value={'green'}>
<ThemeContext.Consumer>
{(value) => <button style={{ background: value }}>Hello Context!</button>}
</ThemeContext.Consumer>
</ThemeContext.Provider>
export default App
I am trying to understand React Context internals , While it is clear how
Context/Provider/Consumer can be used I just don't seem to understand how this line actually works calling a function inside of a component
<ThemeContext.Consumer>
{(value) => <button style={{ background: value }}>Hello Context!</button>}
</ThemeContext.Consumer>
Is it possible have the same pattern work inside of a custom component? This throws a warning 'Functions are not valid as a React child.
<div>
{(value)=><span>{value}</span>}
</div>
React Functions as Child Components
So if I'm getting this right, you are basically asking how you could get a component which is in the following format:
<MyComponent>
{(name) => (
<div>{name}</div>
)}
</MyComponent>
These are called functions as children. You do it by managing the state or a variable in a component locally and you pass that state or variable to any other component in the app by implementing the children as a function in MyComponent.
So your MyComponent component will look something as follows:
class MyComponent extends React.Component {
render() {
return (
<div>
{this.props.children('Scuba Steve')}
</div>
);
}
}
MyComponent.propTypes = {
children: React.PropTypes.func.isRequired,
};
This allows you to reuse MyComponent anywhere with the exact same state or variables but you could render it differently.
You would find this pattern quite a lot in libraries like react-final-form for example, where the library maintains a state and the users can "consume" that state and render it in anyway they want.
You can read more about it at this link and at this link as well.
Understand React Context internals
The React Context Consumer children is a function instead of typical string or React Element
<ThemeContext.Consumer>
{(value) => <button style={{ background: value }}>Hello Context!</button>}
</ThemeContext.Consumer>
<div>
Hey, I'm normal JSX
</div>
The above code will be transpiled to
React.createElement(ThemeContext.Consumer, null, function (value) {
return React.createElement("button", {
style: {
background: value
}
}, "Hello Context!");
})
React.createElement("div", null, "Hey, I'm normal JSX"))
You can see that the children (props.children) is a function.
<div>
{(value)=><span>{value}</span>}
</div>
This code is mean that you declared a function inside <div>. (Not calling that function any more)
<ThemeContext.Consumer>
{(value) => <button style={{ background: value }}>Hello Context!</button>}
</ThemeContext.Consumer>
This function will be called inside ThemeContext.Consumer then your element will render
I see that it as more of a javascript question than a react specific; react components at the end the day will become a function; javascript support function as first-class, so a function can be passed to other function as arguments ( or returned as value ) hence the higher the components and context API. So your question can be roughly translated to this code snippet:
function Theme (color) {
/* some code maybe */
return function Nav (TotalItems){
return `I'll render a ${color} with ${TotalItems} TotalItems`;
}
}
let redThemed = Theme( "dark red");
let navComp = redThemed(17);
console.log( navComp );
console.log( redThemed(12) );
let blueThemed = Theme( "light blue");
console.log( blueThemed(4) );
I recently read an article about performance optimization in React and i just wanted to make sure i understand one of the key concepts.
Let's say i have this component:
class NewComponent extends React.Component {
render() {
return (
<User>
name={"test"}
</User>
)
}
}
If NewComponent's state changes, it will not re-render User, right? Since User will be the same instance with the same String prop every time.
However, if i write something like this:
class NewComponent extends React.Component {
render() {
return (
<User>
onChange={(value1, value2) => {
// do some stuff
}}
name={"test"}
</User>
)
}
}
User will always get re-rendered since React is creating and passing a new instance of the onChange function on every render.
Is that true?
When it's just a component, the same instance is being passed, but when there are objects/arrays/functions involved, React creates a new instance on every parent render?
Without using React.memo for functional components or PureComponents for class components, <User /> will re-render every time <NewComponent /> will receive an updated prop and/or updates its state.
As you can see below, even though props for the <Child /> component don't change, they're still being re-rendered due to the updates in state of the <Parent /> component.
class Parent extends React.Component {
state = { count: 0 };
updateState = () => {
this.setState(prevState => {
return {
count: prevState.count + 1
};
});
};
render() {
console.log(`Parent.count`, this.state.count);
return (
<div>
<button type="button" onClick={this.updateState}>
Update state
</button>
<Child id="1" />
<Child id="2" />
</div>
);
}
}
function Child(props) {
console.log(`Child ${props.id} re-/rendered!`);
return null;
}
ReactDOM.render(
<Parent />,
document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
If you want to skip re-renders, you could use React.memo, which according to React's documentation, "by default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument." Meaning, if you're passing simple scalar values, such as string, number, or boolean, it will skip the re-render. However, if you have more complex objects, this might not work.
Note below how the complexProp, for the second button, causes a re-render, even though it's using React.memo.
class Parent extends React.Component {
state = { count: 0 };
updateState = () => {
this.setState(prevState => {
return {
count: prevState.count + 1
};
});
};
render() {
console.log(`Parent.count`, this.state.count);
return (
<div>
<button type="button" onClick={this.updateState}>
Update state
</button>
<Child id="1" complexProp={() => { console.log(`I am function!`) }} />
<Child id="2" />
</div>
);
}
}
const Child = React.memo(
function Child(props) {
console.log(`Child ${props.id} re-/rendered!`);
return null
}
)
ReactDOM.render(
<Parent />,
document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Read more about shallow comparison:
How does shallow compare work in react - stackoverflow.com
As for your performance question, I'd suggest to be careful with premature optimizations because they could cause performance problems too. If you have visible performance issues that can be measured, then I'd suggest reaching for tools like React.memo and/or PureComponent. Otherwise, I would not worry about it, even though your components re-render - it's not always a bad thing.
Here's a good read how premature optimizations could cause issues:
When to useMemo and useCallback - Kent C. Dodds
Performance optimizations are not free. They ALWAYS come with a cost but do NOT always come with a benefit to offset that cost.
Therefore, optimize responsibly.
We are working on a Laravel project and it is written totally with JavaScript/HTML/JQuery. We are considering migrating to React as Laravel supports it. Is it OK to have many ReactDOM.render() ( per component) in the initial steps so we can progressively and fully convert our code base in the long run?
Is it necessary to access ref or just we can render each component like this:
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<App/>, document.querySelector("#form"))
and use it like this for every component:
// some html code and then
...
<div id="form"> </div>
<div id="modal"> </div>
<div id="navbar"> </div>
...
// rest of html code
Yes this is totally fine, when you call ReactDOM.render multiple times it will basically just triggers a diffing, very similar to the render method of a class component.
I actually wrote an article (with a tutorial) about "Integrate React with other applications and frameworks" that talks exactly about that topic.
The bottom line of this approach is that you can expose a global object with mount and unmount functions of your widget / mini-app and inside it call ReactDOM.render or ReactDOM.unmountComponentAtNode.
window.MyComponent = {
mount: (props, container) => {
ReactDOM.render(<Component {...props} />, container);
},
unmount: (container) => {
ReactDOM.unmountComponentAtNode(container);
}
}
Then in another part in the page you can call these functions, no matter what library or framework you are using obviously.
The beauty here, is that other react applications can use it as if it was a real component:
class MyComponentWrapper extends PureComponent {
// create a ref so we can pass the element to mount and unmount
myRef = React.createRef();
componentDidMount() {
// initial render with props
window.MyComponent.mount(this.props, this.myRef.current);
}
componentDidUpdate(prevProps) {
if(prevProps !== this.props){
window.MyComponent.mount(this.props, this.myRef.current);
}
}
componentWillUnmount(){
window.MyComponent.unmount(this.myRef.current);
}
render() {
return <div ref={this.myRef}></div>
}
}
Hope it helps.
Yes, that's completely fine. React is designed for gradual adoption.
I'm new to React and from the limited tutorials etc. that I've done I've seen that the application state lives at a level that may be different than the component that's rendering it, for the component to be able to edit the state a function is passed in as a property and used that way. This is all fine but at what point is it ridiculous to be passing a function into a nested component? For example, I have
<App>
<Container>
<Item>
<ItemChangeMenu/>
</Item>
</Container>
</App>
App has the state and the function changeItem, should the Container, Item, and ItemChangeMenu all have an onItemChanged property that passes it along?
There are 2 main approaches to pass props down to the children:
Explicit - passing the props as individuals on each level:
const Container = (props) => (
<Item
onItemChange={props.onItemChange}
value={props.value} />
);
const Item = (props) => (
<ItemChangeMenu
onItemChange={props.onItemChange}
value={props.value} />
);
const ItemChangeMenu = (props) => (
<input
onChange={props.onItemChange}
value={props.value} />
);
class App extends React.Component {
state = { value: 'Hello World...' }
onItemChange = ({ target }) => {
this.setState(state => ({ value: target.value }))
}
render() {
const { value } = this.state;
return (
<Container
onItemChange={this.onItemChange}
value={value} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>
Implicit - passing the entire props object but spreading it (new
ES2018 feature):
const Container = (props) => <Item {...props} />;
const Item = (props) => <ItemChangeMenu {...props} />;
const ItemChangeMenu = (props) => (
<input
onChange={props.onItemChange}
value={props.value} />
);
class App extends React.Component {
state = { value: 'Hello World...' }
onItemChange = ({ target }) => {
this.setState(state => ({ value: target.value }))
}
render() {
const { value } = this.state;
return (
<Container
onItemChange={this.onItemChange}
value={value} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>
There are pros and cons for each pattern of course.
With the explicit pattern, you get a clear view of which props are being passed down, and you can manually decide what props you want to pass.
The main caveat with this approach though, it is tedious and quite hard to refactor.
With the implicit pattern, you just pass an object and forget about it, you need to pass a new prop? it will already go down the chimney. If you ever need to refactor, change names of props or re-order the components hierarchy, then no need to change the props names.
However, the main caveat with this approach, is that you will probably pass more props then you need to. this can lead to unnecessary calls for componentWillReceiveProps.
Of course you can use destructure to minimize it:
const {someProp, someOtherProp, ...rest} = this.props;
// ...
<Child {...rest} />
But this is almost the same thing as taking the explicit approach.
There is a 3rd option though it is not related to the props API.
It called context. With the context API you can "skip" levels and grab objects directly from the grand parents.
Sounds great, but if you will look at the DOCS:
Why Not To Use Context
The vast majority of applications do not need
to use context.
If you want your application to be stable, don’t use context. It is an
experimental API and it is likely to break in future releases of
React.
As said in the docs, this is an "experimental API and it is likely to break" and that day has come, a new context API is on it's way and will might change the way we work with props or react all together.
The context feature is not something that no one use by the way, important libraries like react-redux, react-router, react-mobx are using and dependent on it.