typescript.react.best-practice.react-props-in-state.react-props-in-state

profile photo of semgrepsemgrep
Author
4,056
Download Count*

Copying a prop into state in React -- this is bad practice as all updates to it are ignored. Instead, read props directly in your component and avoid copying props into state.

Run Locally

Run in CI

Defintion

rules:
  - id: react-props-in-state
    pattern-either:
      - patterns:
          - pattern-inside: |
              class $CN extends React.Component {
                ...
              }
          - pattern-either:
              - pattern: |
                  state = {$NAME: <... this.props.$PROP ...>}
              - pattern: |
                  this.state = {$NAME: <... this.props.$PROP ...>}
          - metavariable-regex:
              metavariable: $NAME
              regex: ^(?!default|initial).*$
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  function $FN({$PROP},...) {
                    ...
                  }
              - pattern-inside: |
                  function $FN($PROP,...) {
                    ...
                  }
          - pattern-either:
              - pattern: useState(<... $PROP ...>)
              - pattern: useState(<... $PROP.$KEY ...>)
              - pattern: |
                  useState(function $X(...) {
                    ...
                    <... $PROP ...>
                    ...
                  })
              - pattern: |
                  useState(function $X(...) {
                    ...
                    <... $PROP.$KEY ...>
                    ...
                  })
          - metavariable-regex:
              metavariable: $PROP
              regex: ^(?!default|initial).*$
    message: Copying a prop into state in React -- this is bad practice as all
      updates to it are ignored. Instead, read props directly in your component
      and avoid copying props into state.
    metadata:
      references:
        - https://overreacted.io/writing-resilient-components/#principle-1-dont-stop-the-data-flow
      category: best-practice
      technology:
        - react
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    languages:
      - typescript
      - javascript
    severity: WARNING

Examples

react-props-in-state.jsx

class Test1 extends React.Component {
  constructor() {
    // ruleid:react-props-in-state
    this.state = {
          foo: 'bar',
          color: this.props.color,
          one: 1
    };
  }

  render() {
    const { color } = this.state;
    return (
      <button className={'Button-' + color}>
        {this.props.children}
      </button>
    );
  }
}

class Test2 extends React.Component {
  constructor() {
    // ruleid:react-props-in-state
    this.state = {
      textColor: slowlyCalculateTextColor(this.props.color)
    };
  }

  render() {
    return (
      <button className={
        'Button-' + this.props.color +
        ' Button-text-' + this.state.textColor
      }>
        {this.props.children}
      </button>
    );
  }
}

class OkTest extends React.Component {
// ok: react-props-in-state
  constructor() {
    this.state = {
          foo: 'bar',
          initialColor: this.props.color,
          one: 1
    };
  }

  render() {
    const { color } = this.state;
    return (
      <button className={'Button-' + color}>
        {this.props.children}
      </button>
    );
  }
}

function Test3({ text }) {
  // ruleid:react-props-in-state
  const [buttonText] = useState(text)
  return <button>{buttonText}</button>
}

function Test4(props) {
  // ruleid:react-props-in-state
  const [formattedText] = useState(() => slowlyFormatText(props.text))
  return <button>{formattedText}</button>
}

function OkTest1({ color, children }) {
  const textColor = useMemo(
// ok: react-props-in-state
    () => slowlyCalculateTextColor(color),
    [color]
  );
  return (
    <button className={'Button-' + color + ' Button-text-' + textColor}>
      {children}
    </button>
  );
}

class OkTest2 extends React.PureComponent {
  render() {
// ok: react-props-in-state
    const textColor = slowlyCalculateTextColor(this.props.color);
    return (
      <button className={
        'Button-' + this.props.color +
        ' Button-text-' + textColor
      }>
        {this.props.children}
      </button>
    );
  }
}

react-props-in-state.tsx

class Test1 extends React.Component {
  constructor() {
    // ruleid:react-props-in-state
    this.state = {
          foo: 'bar',
          color: this.props.color,
          one: 1
    };
  }

  render() {
    const { color } = this.state;
    return (
      <button className={'Button-' + color}>
        {this.props.children}
      </button>
    );
  }
}

class Test2 extends React.Component {
  constructor() {
    // ruleid:react-props-in-state
    this.state = {
      textColor: slowlyCalculateTextColor(this.props.color)
    };
  }

  render() {
    return (
      <button className={
        'Button-' + this.props.color +
        ' Button-text-' + this.state.textColor
      }>
        {this.props.children}
      </button>
    );
  }
}

class OkTest extends React.Component {
// ok: react-props-in-state
  constructor() {
    this.state = {
          foo: 'bar',
          initialColor: this.props.color,
          one: 1
    };
  }

  render() {
    const { color } = this.state;
    return (
      <button className={'Button-' + color}>
        {this.props.children}
      </button>
    );
  }
}

function Test3({ text }) {
  // ruleid:react-props-in-state
  const [buttonText] = useState(text)
  return <button>{buttonText}</button>
}

function Test4(props) {
  // ruleid:react-props-in-state
  const [formattedText] = useState(() => slowlyFormatText(props.text))
  return <button>{formattedText}</button>
}

function OkTest1({ color, children }) {
  const textColor = useMemo(
// ok: react-props-in-state
    () => slowlyCalculateTextColor(color),
    [color]
  );
  return (
    <button className={'Button-' + color + ' Button-text-' + textColor}>
      {children}
    </button>
  );
}

class OkTest2 extends React.PureComponent {
  render() {
// ok: react-props-in-state
    const textColor = slowlyCalculateTextColor(this.props.color);
    return (
      <button className={
        'Button-' + this.props.color +
        ' Button-text-' + textColor
      }>
        {this.props.children}
      </button>
    );
  }
}

function OkTest3({ initialText }) {
  // ok: react-props-in-state
  const [buttonText] = useState(initialText)
  return <button>{buttonText}</button>
}