CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-eslint-plugin-react

React specific linting rules for ESLint

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

component-lifecycle-rules.mddocs/

Component Lifecycle Rules

Rules for React component lifecycle methods, state management, and component update patterns. These rules ensure proper usage of lifecycle methods and prevent common state-related issues.

Capabilities

State Management Rules

Rules for safe and proper state management in React components.

const stateRules = {
  /** Prevent usage of setState in componentDidMount */
  'react/no-did-mount-set-state': ESLintRule;
  /** Prevent usage of setState in componentDidUpdate */
  'react/no-did-update-set-state': ESLintRule;
  /** Prevent usage of setState in componentWillUpdate */
  'react/no-will-update-set-state': ESLintRule;
  /** Prevent using this.state within a this.setState */
  'react/no-access-state-in-setstate': ESLintRule;
  /** Enforce that setState is used properly */
  'react/state-in-constructor': ESLintRule;
};

Usage Examples:

// ✓ Good - Async operations without setState in componentDidMount
class MyComponent extends React.Component {
  componentDidMount() {
    this.fetchData(); // Async operation that will setState elsewhere
  }
  
  async fetchData() {
    try {
      const data = await api.getData();
      this.setState({ data }); // setState in separate method is OK
    } catch (error) {
      this.setState({ error });
    }
  }
}

// ✗ Bad - setState directly in componentDidMount
class MyComponent extends React.Component {
  componentDidMount() {
    this.setState({ mounted: true }); // Can cause unnecessary re-render
  }
}

// ✓ Good - Conditional updates in componentDidUpdate
class MyComponent extends React.Component {
  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      this.fetchUserData();
    }
  }
}

// ✗ Bad - setState in componentDidUpdate without conditions
class MyComponent extends React.Component {
  componentDidUpdate(prevProps) {
    this.setState({ updated: true }); // Can cause infinite loop
  }
}

// ✓ Good - Using functional setState to avoid state dependency
this.setState(prevState => ({
  count: prevState.count + 1
}));

// ✗ Bad - Reading current state in setState
this.setState({
  count: this.state.count + 1 // Race condition potential
});

Lifecycle Method Usage

Rules for proper usage of React lifecycle methods and avoiding deprecated patterns.

const lifecycleRules = {
  /** Prevent usage of componentWillMount, componentWillReceiveProps, componentWillUpdate */
  'react/no-deprecated': ESLintRule;
  /** Prevent usage of UNSAFE_ lifecycle methods */
  'react/no-unsafe': ESLintRule;
  /** Enforce ES5 or ES6 class for returning value in render function */
  'react/require-render-return': ESLintRule;
  /** Prevent usage of shouldComponentUpdate when extending React.PureComponent */
  'react/no-redundant-should-component-update': ESLintRule;
  /** Prevent usage of arrow functions in lifecycle methods */
  'react/no-arrow-function-lifecycle': ESLintRule;
};

Usage Examples:

// ✓ Good - Using modern lifecycle methods
class MyComponent extends React.Component {
  static getDerivedStateFromProps(props, state) {
    if (props.value !== state.previousValue) {
      return {
        derivedValue: props.value * 2,
        previousValue: props.value
      };
    }
    return null;
  }
  
  componentDidMount() {
    this.setupComponent();
  }
  
  componentDidUpdate(prevProps, prevState) {
    if (prevState.data !== this.state.data) {
      this.processData();
    }
  }
  
  render() {
    return <div>{this.state.derivedValue}</div>;
  }
}

// ✗ Bad - Using deprecated lifecycle methods
class MyComponent extends React.Component {
  componentWillMount() {
    // Deprecated and unsafe
    this.setupComponent();
  }
  
  componentWillReceiveProps(nextProps) {
    // Deprecated and unsafe
    this.setState({ value: nextProps.value });
  }
  
  componentWillUpdate(nextProps, nextState) {
    // Deprecated and unsafe
    console.log('About to update');
  }
}

// ✓ Good - Proper render method with return
class MyComponent extends React.Component {
  render() {
    return <div>Content</div>;
  }
}

// ✗ Bad - Render method without return
class MyComponent extends React.Component {
  render() {
    console.log('rendering');
    // Missing return statement
  }
}

// ✓ Good - Regular methods, not arrow functions for lifecycle
class MyComponent extends React.Component {
  componentDidMount() {
    this.initializeComponent();
  }
  
  // Arrow function for event handlers is OK
  handleClick = () => {
    this.setState({ clicked: true });
  }
}

// ✗ Bad - Arrow function for lifecycle methods
class MyComponent extends React.Component {
  componentDidMount = () => {
    this.initializeComponent();
  }
}

PureComponent and Performance Rules

Rules for optimizing component performance and avoiding redundant updates.

const performanceRules = {
  /** Prevent usage of shouldComponentUpdate when extending React.PureComponent */
  'react/no-redundant-should-component-update': ESLintRule;
  /** Enforce usage of optimization like PureComponent or React.memo */
  'react/require-optimization': ESLintRule;
  /** Prevent unused state */
  'react/no-unused-state': ESLintRule;
  /** Prevent unused class component methods */
  'react/no-unused-class-component-methods': ESLintRule;
};

Usage Examples:

// ✓ Good - PureComponent without shouldComponentUpdate
class MyComponent extends React.PureComponent {
  render() {
    return <div>{this.props.data}</div>;
  }
}

// ✗ Bad - PureComponent with shouldComponentUpdate (redundant)
class MyComponent extends React.PureComponent {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.data !== this.props.data;
  }
  
  render() {
    return <div>{this.props.data}</div>;
  }
}

// ✓ Good - Component with shouldComponentUpdate
class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.data !== this.props.data;
  }
  
  render() {
    return <div>{this.props.data}</div>;
  }
}

// ✓ Good - All state properties used
class MyComponent extends React.Component {
  state = {
    count: 0,
    name: ''
  };
  
  render() {
    return (
      <div>
        <span>{this.state.count}</span>
        <span>{this.state.name}</span>
      </div>
    );
  }
}

// ✗ Bad - Unused state property
class MyComponent extends React.Component {
  state = {
    count: 0,
    unused: 'never referenced' // This state is never used
  };
  
  render() {
    return <div>{this.state.count}</div>;
  }
}

State Initialization and Constructor Rules

Rules for proper state initialization patterns.

const initializationRules = {
  /** Enforce state initialization in constructor vs class property */
  'react/state-in-constructor': ESLintRule;
  /** Enforce consistent placement of static properties */
  'react/static-property-placement': ESLintRule;
};

Usage Examples:

// ✓ Good - State in constructor (when rule enforces constructor)
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      loading: false
    };
  }
}

// ✓ Good - State as class property (when rule allows class properties)
class MyComponent extends React.Component {
  state = {
    count: 0,
    loading: false
  };
}

// Configuration determines which pattern is enforced:
{
  "rules": {
    "react/state-in-constructor": ["error", "always"], // Enforce constructor
    // or
    "react/state-in-constructor": ["error", "never"]   // Enforce class property
  }
}

// ✓ Good - Static properties at top (when configured)
class MyComponent extends React.Component {
  static propTypes = {
    name: PropTypes.string
  };
  
  static defaultProps = {
    name: 'Anonymous'
  };
  
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  render() {
    return <div>{this.props.name}</div>;
  }
}

Functional Component Lifecycle

Rules for lifecycle-equivalent patterns in functional components.

const functionalLifecycleRules = {
  /** Enforce destructuring and symmetric naming of useState hook value and setter */
  'react/hook-use-state': ESLintRule;
};

Usage Examples:

// ✓ Good - Proper useState destructuring with symmetric naming
const MyComponent = () => {
  const [count, setCount] = useState(0);
  const [isVisible, setIsVisible] = useState(false);
  const [userData, setUserData] = useState(null);
  
  useEffect(() => {
    // Effect logic
  }, [count]);
  
  return (
    <div>
      <span>Count: {count}</span>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

// ✗ Bad - Non-symmetric naming for useState
const MyComponent = () => {
  const [count, updateCount] = useState(0); // Should be setCount
  const [isVisible, changeVisibility] = useState(false); // Should be setIsVisible
  
  return (
    <div>
      <span>Count: {count}</span>
      <button onClick={() => updateCount(count + 1)}>Increment</button>
    </div>
  );
};

Error Boundaries and Error Handling

Rules related to error handling in component lifecycle.

const errorHandlingRules = {
  /** Prevent usage of isMounted */
  'react/no-is-mounted': ESLintRule;
  /** Prevent usage of the return value of React.render */
  'react/no-render-return-value': ESLintRule;
};

Usage Examples:

// ✓ Good - Error boundary with proper lifecycle methods
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.log('Error caught:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong.</div>;
    }
    
    return this.props.children;
  }
}

// ✓ Good - Avoiding isMounted anti-pattern
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null };
    this._isMounted = false;
  }
  
  componentDidMount() {
    this._isMounted = true;
    this.fetchData();
  }
  
  componentWillUnmount() {
    this._isMounted = false;
  }
  
  async fetchData() {
    const data = await api.getData();
    if (this._isMounted) {
      this.setState({ data });
    }
  }
}

// ✗ Bad - Using isMounted (deprecated and unreliable)
class MyComponent extends React.Component {
  async fetchData() {
    const data = await api.getData();
    if (this.isMounted()) {
      this.setState({ data });
    }
  }
}

Lifecycle Rule Configuration

Lifecycle rules often support configuration for different coding styles:

{
  "rules": {
    "react/no-did-mount-set-state": ["error", "disallow-in-func"],
    "react/no-did-update-set-state": ["error", "disallow-in-func"],
    "react/state-in-constructor": ["error", "always"],
    "react/static-property-placement": ["error", "property assignment"],
    "react/hook-use-state": ["error", {
      "allow": ["count", "setCount"] // Allow specific patterns
    }],
    "react/require-optimization": ["error", {
      "allowDecorators": ["observer"] // Allow certain HOCs to skip optimization
    }]
  }
}

docs

code-style-rules.md

component-lifecycle-rules.md

index.md

jsx-syntax-rules.md

plugin-configuration.md

prop-validation-rules.md

react-component-rules.md

security-safety-rules.md

tile.json