no-unstable-default-props
Full Name in @eslint-react/eslint-plugin
@eslint-react/no-unstable-default-propsFull Name in eslint-plugin-react-x
react-x/no-unstable-default-propsPresets
strict
strict-typescript
strict-type-checked
Features
⚙️
Description
Prevents using referential-type values as default props in object destructuring.
When using object destructuring syntax you can set the default value for a given property if it does not exist. If you set the default value to one of the values that is compared by identity, then each time the destructuring is evaluated, the JS engine will create a new, distinct value in the destructured variable.
This harms performance as it means that React will have to re-evaluate hooks and re-render memoized components more often than necessary.
To fix the violations, the easiest way is to use a referencing variable in module scope instead of using the literal values.
Rule Options
This rule has a single options object with the following property:
safeDefaultProps(default:[]): An array of identifier names or regex patterns that are safe to use as default props.
Examples
Failing
import React from "react";
interface MyComponentProps {
items: string[];
}
function MyComponent({ items = [] }: MyComponentProps) {
// ^^
// - A/an 'Array literal' as default prop. This could lead to potential infinite render loop in React. Use a variable instead of 'Array literal'.
return null;
}import React from "react";
interface MyComponentProps {
items: Record<string, string>;
}
function MyComponent({ items = {} }: MyComponentProps) {
// ^^
// - A/an 'Object literal' as default prop. This could lead to potential infinite render loop in React. Use a variable instead of 'Object literal'.
return null;
}import React from "react";
interface MyComponentProps {
onClick: () => void;
}
function MyComponent({ onClick = () => {} }: MyComponentProps) {
// ^^^^^^^^
// - A/an 'arrow function expression' as default prop. This could lead to potential infinite render loop in React. Use a variable instead of 'arrow function expression'.
return null;
}import React from "react";
interface MyComponentProps {
items: string[];
}
function MyComponent(props: MyComponentProps) {
const { items = [] } = props;
// ^^
// - A/an 'Array literal' as default prop. This could lead to potential infinite render loop in React. Use a variable instead of 'Array literal'.
return null;
}Passing
import React from "react";
const emptyArray: string[] = [];
interface MyComponentProps {
items: string[];
}
function MyComponent({ items = emptyArray }: MyComponentProps) {
return null;
}import React from "react";
const emptyObject = {};
interface MyComponentProps {
items: Record<string, string>;
}
function MyComponent({ items = emptyObject }: MyComponentProps) {
return null;
}import React from "react";
const noop = () => {};
interface MyComponentProps {
onClick: () => void;
}
function MyComponent({ onClick = noop }: MyComponentProps) {
return null;
}import React from "react";
const emptyArray: string[] = [];
interface MyComponentProps {
items: string[];
}
function MyComponent(props: MyComponentProps) {
const { items = emptyArray } = props;
return null;
}import React from "react";
interface MyComponentProps {
num: number;
str: string;
bool: boolean;
}
function MyComponent({ num = 3, str = "foo", bool = true }: MyComponentProps) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// - Primitives are all compared by value, so are safe to be inlined as default props.
return null;
}Examples with safeDefaultProps
This option allows you to allowlist specific constructor or factory method identifiers that create value-type objects safe to use as default props.
Configuration
{
"@eslint-react/no-unstable-default-props": ["error", {
"safeDefaultProps": ["Vector3", "Color3", "vector", "/^Immutable.*/"]
}]
}Failing
import React from "react";
interface MyComponentProps {
position: Vector3;
}
// Without configuration, this would fail
function MyComponent({ position = new Vector3(0, 0, 0) }: MyComponentProps) {
// ^^^^^^^^^^^^^^^^^^^^^^
// - A/an 'new expression' as default prop.
return null;
}import React from "react";
interface MyComponentProps {
cache: Cache;
}
// CustomCache is not in the allowlist, so this still fails
function MyComponent({ cache = new CustomCache() }: MyComponentProps) {
// ^^^^^^^^^^^^^^^^^
// - A/an 'new expression' as default prop.
return null;
}import React from "react";
interface MyComponentProps {
items: string[];
}
// Object and array literals always fail regardless of configuration
function MyComponent({ items = [] }: MyComponentProps) {
// ^^
// - A/an 'array expression' as default prop.
return null;
}Passing
import React from "react";
interface MyComponentProps {
position: Vector3;
}
// Vector3 is in the allowlist, so constructor calls are allowed
function MyComponent({ position = new Vector3(0, 0, 0) }: MyComponentProps) {
return null;
}import React from "react";
interface MyComponentProps {
color: Color3;
}
// Color3 is in the allowlist, so factory methods are allowed
function MyComponent({ color = Color3.Red() }: MyComponentProps) {
return null;
}import React from "react";
interface MyComponentProps {
position: Vector3;
}
// 'vector' is in the allowlist, so member expression calls are allowed
function MyComponent({ position = vector.create(0, 0, 0) }: MyComponentProps) {
return null;
}import React from "react";
interface MyComponentProps {
data: ImmutableMap<string, number>;
}
// Matches the regex pattern /^Immutable.*/
function MyComponent({ data = ImmutableMap.of() }: MyComponentProps) {
return null;
}import React from "react";
interface MyComponentProps {
list: ImmutableList<string>;
}
// Also matches the regex pattern /^Immutable.*/
function MyComponent({ list = ImmutableList.of("a", "b") }: MyComponentProps) {
return null;
}Implementation
See Also
no-unstable-context-value
Prevents non-stable values (i.e. object literals) from being used as a value forContext.Provider.