Skip to content

destructuring-assignment for different component types #1709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
HenriBeck opened this issue Mar 1, 2018 · 25 comments
Closed

destructuring-assignment for different component types #1709

HenriBeck opened this issue Mar 1, 2018 · 25 comments

Comments

@HenriBeck
Copy link

I would like to propose an option for allowing different behaviors for a functional component and a class component for the destructuring-assignment rule.

@ljharb
Copy link
Member

ljharb commented Mar 1, 2018

Can you elaborate, with code examples, and explain why you’d want it to be different?

@HenriBeck HenriBeck changed the title react/destructuring-assignment for different component types destructuring-assignment for different component types Mar 2, 2018
@HenriBeck
Copy link
Author

Personally, I like to destructure my props in a functional component, for the simple reason of passing additional extra props down to a specific child. Though I tend to not do that for class components.
Just having the option would be nice.
Of course, it would still allow for just a string as the first configuration. This would just add a few extra configuration options.

New rule configuration example:

module.exports = {
  rules: {
    'react/destructuring-assignment': ['error', {
      functional: 'always',
      class: 'never',
    }],
  },
};

The correct code for the configuration example:

function FunctionalComponent({ children }) {
  return children;
}

class Component extends React.Component {
  render() {
    return this.props.children;
  }
}

Incorrect code for the configuration example:

class Component extends React.Component {
  render() {
    const { children } = this.props;

    return children;
  }
}

@ljharb
Copy link
Member

ljharb commented Mar 2, 2018

We'd need to also account for createClass components; but if we had all three, and all three defaulted to true (or, we had an "ignore" object with all three defaulting to "false", which is probably better) that could work.

@HenriBeck
Copy link
Author

I would personally prefer rather allowing an object for more detailed configuration rather than turning the rule off for certain component types.

// Still support configuration for all three component types
module.exports = {
  rules: {
    'react/destructuring-assignment': ['error',  'always'],
  },
};

// Individual configuration for the component types
module.exports = {
  rules: {
    'react/destructuring-assignment': ['error', {
      functional: 'always',
      class: 'never',
      createClass: 'always',
    }],
  },
};

// Or allow for specific component overrides
module.exports = {
  rules: {
    'react/destructuring-assignment': ['error', 'always', {
      class: 'never',
    }],
  },
};

@ljharb
Copy link
Member

ljharb commented Mar 2, 2018

Using always/never seems most appropriate here. Want to make a PR?

@HenriBeck
Copy link
Author

Sure, I will start working on it next week. Which configuration should I implement?

@ljharb
Copy link
Member

ljharb commented Mar 2, 2018

The always/never one you suggested, with "class", "createClass", and "SFC" as options, would be great.

@HenriBeck
Copy link
Author

HenriBeck commented Mar 3, 2018

Also, I discovered this bug:
In both cases when destructuring is set to always it's not reporting an error.

function Baz({ ...rest }) {
  return (<div>{rest.children}</div>);
}


function Baz(properties) {
  return (<div>{properties.children}</div>);
}

Also is this intended?

class Comp extends React.Component {
  state = {
    animationName: this.props.active // Requires destructuring here when set to always
      ? 'fade-in'
      : null,
  };
}

Also when using something like react-jss it reports when using functions.

@ljharb
Copy link
Member

ljharb commented Mar 4, 2018

The first one - { ...rest } - seems like a hack around the rule; I think we should change the rule to add an option that forbids accessing properties off of a rest prop. (in other words, rest.children would be an error).

The second one is a bug - the argument shouldn't need to be named "props" to be caught.

As for the third one, i'd say it's an antipattern anyways to have logic like that in a class field, because you'd also need duplicate logic in componentWillReceiveProps, so I think the proper fix for that is "move that to the constructor".

@HenriBeck
Copy link
Author

HenriBeck commented Mar 5, 2018

Why would I not need to duplicate the logic in componentWillReceiveProps when I move the state initialization into the constructor?

I don't know if this is intended but for class methods, it's also reporting to destructure the props.

@ljharb
Copy link
Member

ljharb commented Mar 5, 2018

You do, always (with state that depends on props), need to duplicate the logic in two places; at construction and in cWRP. I'm suggesting that it would be cleaner code to have the initialization-time part in the constructor rather than in a class field.

@HenriBeck
Copy link
Author

Exactly, I personally prefer to have the initialization logic in class fields as it's not so much typing.
Either way, it shouldn't report an error there.

I will start working on a rewrite of the rule with the above-mentioned changes in mind, alright?

@ljharb
Copy link
Member

ljharb commented Mar 7, 2018

I’m not convinced it shouldn’t report an error there; i tend to think this use case should not be permitted in a class field.

@HenriBeck
Copy link
Author

There is no way of destructuring inside a class field so it definitely shouldn't report an error here.

@ljharb
Copy link
Member

ljharb commented Mar 9, 2018

Correct; the error means you're not allowed to use a class field for this case.

@joggienl
Copy link

joggienl commented Jun 22, 2018

Just to be clear, does that mean that the example below (regardless what it does or implies it does) is not "allowed" and that I need to add the assignment for example to the constructor and in cWRP?

An approach like below is causing an error on one of my projects.

import React from 'react';
import PropTypes from 'prop-types';

class SomeComponent extends React.Component {
  // Next line triggers
  // "Must use destructuring context assignment  react/destructuring-assignment"
  example = this.context.someProp;

  static propTypes = {
    example: PropTypes.string.isRequired,
  };

  static contextTypes = {
    someProp: PropTypes.string.isRequired,
  };

  render() {
    const { example } = this.props;
    const { someProp } = this.context;

    return (
      <React.Fragment>
        someProp equals example?
        {someProp === example}
      </React.Fragment>
    );
  }
}

export default SomeComponent;

@kamronbatman
Copy link

I am having the same problem:

import React from 'react';
import PropTypes from 'prop-types';

class SomeComponent extends React.Component {
  state = {
    example: this.props.example, // triggers
  };

  static propTypes = {
    example: PropTypes.string.isRequired,
  };

  render() {
    ...
  }
}

export default SomeComponent;

Also triggers on:
state = someFunctionHere(this.props.example);

@ljharb
Copy link
Member

ljharb commented Jun 28, 2018

@joggienl yes, that's what that means. you should use a constructor for that case.

@kamronbatman as should you. also note, your SomeComponent has a bug - it's setting state based on initial props, but you don't have a componentWillReceiveProps or getDerivedStateFromProps, so your component will be broken on rerender.

@kamronbatman
Copy link

kamronbatman commented Jun 28, 2018

It is a terribly simplified example, but I assumed you would understand what I meant.
I hate having to use a constructor if I don't need to, blah.
It would be nice to have an option to skip class body property assignments.

@ljharb
Copy link
Member

ljharb commented Jun 28, 2018

I think that's a needed constructor. However, I do think adding this as an option also makes sense.

#1740 might need to be merged first, however.

@HenriBeck
Copy link
Author

#1740 Is pretty much ready to go, maybe a last review before merging.

About the constructor, I already fixed this but adding an option seems like a good idea.

@kamronbatman
Copy link

@HenriBeck, #1740 looks good but it has merge conflicts.

@HenriBeck
Copy link
Author

Yes, will resolve them tomorrow

@alexandernanberg
Copy link
Contributor

There is now a separate issue for the issues with class properties #1875 (didn’t see this one until now)

@ljharb
Copy link
Member

ljharb commented Feb 4, 2022

#1875 is closed but #1740 was closed without being merged.

It's been awhile, and I'm still not clear on what needs solving. If there's still any problems, please file a new issue.

@ljharb ljharb closed this as completed Feb 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants