Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

add rule for no setState in componentDidUpdate and tests #433

Merged
merged 6 commits into from
May 17, 2020

Conversation

VictorHom
Copy link
Contributor

rule

from the sheet in #341

the rule is for ES5, but I added checks for ES6 class components. Let me know if that's not correct. Happy to make changes.

@sebmck
Copy link
Contributor

sebmck commented May 16, 2020

This only covers setState in very specific positions. This rule could be simplified with only:

function inComponentDidUpdate(path: Path): boolean {
  return path.findAncestry(({node}) => node.type === 'ClassMethod' && node.key.type === 'StaticPropertyKey' && node.key.value.type === 'Identifier' && node.key.value.name === 'componentDidUpdate') !== undefined;
}

if (doesNodeMatchPattern(node, 'this.setState') && inComponentDidUpdate(path)) {
  // Add diagnostic
}

I wouldn't worry about checking if the class extends matches React.Component or Component since a componentDidUpdate method is already enough signal that we're in a component.

@VictorHom
Copy link
Contributor Author

VictorHom commented May 16, 2020

@sebmck , so it sounds like the createClass/createReactClass case isn't something we should write against in tests then? (from checking your snippet)

One question from the docs: https://reactjs.org/docs/react-component.html#componentdidupdate

You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop. It would also cause an extra re-rendering which, while not visible to the user, can affect the component performance. If you’re trying to “mirror” some state to a prop coming from above, consider using the prop directly instead. Read more about why copying props into state causes bugs.

Should wrapped this.setState be an exception in the lint rule?

@sebmck
Copy link
Contributor

sebmck commented May 16, 2020

Yeah don't bother handling createClass or createReactClass.

To handle the conditional case you could do:

import {isFunctionNode, isConditional} from '@romejs/js-ast-utils';

function inComponentDidUpdate(path: Path): boolean {
  const func = path.findAncestry(({node}) => isFunctionNode(node) || isConditional(node));
  return func !== undefined && func.type === 'ClassMethod' && func.key.type === 'StaticPropertyKey' && func.key.value.type === 'Identifier' && func.key.value.name === 'componentDidUpdate';
}

if (doesNodeMatchPattern(node, 'this.setState') && inComponentDidUpdate(path)) {
  // Add diagnostic
}

@VictorHom VictorHom force-pushed the noDidUpdateSetState branch from a7bb4d2 to b6d2c7a Compare May 17, 2020 16:17
@VictorHom
Copy link
Contributor Author

@sebmck had to modify your suggestion with 2 path.findAncestry calls. let me know if that doesn't work

@sebmck sebmck merged commit b37cea8 into rome:master May 17, 2020
@sebmck
Copy link
Contributor

sebmck commented May 17, 2020

Looks good to me. We're only incurring the findAncestry overhead when the node matches this.setState so this is probably as fast as it could be.

@sebmck
Copy link
Contributor

sebmck commented May 17, 2020

Thank you!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants