Linter · Rules
react-perf/jsx-no-new-function-as-prop
Disallow functions created during render as the value of a JSX prop
A function written inside a component is a new function on every render. When you pass it as a prop, it's never equal to the value from the last render, so a child wrapped in React.memo renders again even though the function does the same thing.
The rule reports arrow functions, function expressions, .bind() calls, Function() and new Function(). A function at the top level of a module isn't reported, because it's created once.
Configure
Off by default. To turn it on:
[lint.rules]
"react-perf/jsx-no-new-function-as-prop" = "error" # or "warn"
Every option, set to its default:
[lint.rules]
"react-perf/jsx-no-new-function-as-prop" = ["error", {
nativeAllowList = [],
}]
Examples
Reported:
function Editor({ save }) {
return <Button onClick={() => save("draft")} />;
}
class Editor extends React.Component {
render() {
return <Button onClick={this.save.bind(this)} />;
}
}
The rule looks into ||, &&, ?? and ? ::
function Editor({ onSave }) {
return <Button onClick={onSave || (() => {})} />;
}
A function declared inside the component has the same problem as one written in the prop. The report is on the declaration:
function Editor({ save }) {
const saveDraft = () => save("draft");
function publish() {
save("public");
}
return <Buttons onSave={saveDraft} onPublish={publish} />;
}
Not reported:
function reload() {
location.reload();
}
function Editor() {
return <Button onClick={reload} />;
}
If the function uses props or state, cache it with useCallback:
import { useCallback } from "react";
function Editor({ save }) {
const saveDraft = useCallback(() => save("draft"), [save]);
return <Button onClick={saveDraft} />;
}
In a class, make the handler a property and pass that:
class Editor extends React.Component {
save = () => {
this.props.save("draft");
};
render() {
return <Button onClick={this.save} />;
}
}
Options
| Option | Default | What it does |
|---|---|---|
nativeAllowList | [] | Props to skip on elements with a lowercase name, such as <button>. A list of prop names (case doesn't matter), or "all" for every prop. |
A DOM element doesn't compare its props to skip a render, so a new function costs nothing there. With { "nativeAllowList": "all" }, this is allowed:
function Editor({ save }) {
return <button onClick={() => save("draft")}>Save</button>;
}
Components are still checked:
function Editor({ save }) {
return <Button onClick={() => save("draft")} />;
}
When not to use it
A new prop value only matters to a child that compares its props, for example with React.memo. The React Compiler memoizes these values for you.