or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

code-style-rules.mdcomponent-lifecycle-rules.mdindex.mdjsx-syntax-rules.mdplugin-configuration.mdprop-validation-rules.mdreact-component-rules.mdsecurity-safety-rules.md
tile.json

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
    }]
  }
}