Skip to content
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

[New Rule] prefer-function-component #3040

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

tatethurston
Copy link

@tatethurston tatethurston commented Aug 11, 2021

Adds a new rule to enforce the use of function components over class components. Related issue: #2860

Users wanting to try this lint rule today:

This functionality exists in eslint-plugin-react-prefer-function-component.

npm install eslint-plugin-react-prefer-function-component

@ljharb
Copy link
Member

ljharb commented Aug 12, 2021

#2860 doesn't have a "help wanted" or "accepted" label on it - I continue to be unconvinced this is actually a good rule to have. Some things must be class components, and other things are more clear/better as class components; I don't think a lint rule is naunced enough to be appropriate.

That said, I did ask you to open the PR, so I'll give it a good faith review.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if perhaps this could be a more generic "prefer-component-style" rule, that can be used to force class components or force function components?

Since the addition of hooks, it has been possible to write stateful React components
using only functions. Mixing both class and function components in a code base adds unnecessary hurdles for sharing reusable logic.

By default, class components that use `componentDidCatch` are enabled because there is currently no hook alternative for React. This option is configurable via `allowComponentDidCatch`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not clear on why this is an option. A class component that's an error boundary can never be a functional component (pending changes from React itself), so there'd never be any value in this rule warning on it. I think that this option shouldn't be an option, it should just be hardcoded to true.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many codebases I've seen don't implement error boundaries from scratch in their code base, instead they import library solutions that use error boundaries internally. I made this configurable (though with the default value of true so most clients can ignore it) for clients who want all class component usage to be flagged. This also provides a migration path should React release functional component error boundaries.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, Preact does offer functional error boundaries via a hook, so this would be valuable for those users.

suggestion: false,
url: docsUrl('prefer-function-component')
},
fixable: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason this rule isn't fixable?

Any component with a constructor body or class methods, of course, couldn't be fixable, but the example in the readme could.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially fixable seemed odd to me on first thought, but the same logic as used by prefer-stateless-function could be used here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of rules are partially fixable; obv it's ideal when things are fully fixable tho.

Actually wait, I'd forgotten about prefer-stateless-function. How is this rule different from that one? https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think #2860 explains the difference well:

The prefer-stateless-function rule is not enough for this case, since it allows class components provided they have a state or additional methods (like handlers defined as a class methods). It is not possible to forbid the use of state altogether (you can disable setState, but defining state is still allowed, silencing prefer-stateless-function), and filtering class methods using eslint rules without interfering with other code is complicated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see, so this is a stronger thing than that, where prefer-stateless-function pushes SFCs when possible, this rule would ban class components entirely in favor of hook-using SFCs. Gotcha.

@tatethurston
Copy link
Author

I wonder if perhaps this could be a more generic "prefer-component-style" rule, that can be used to force class components or force function components?

Yeah that seems reasonable to me, the motivating intent here is to keep consistency in a code base.

Looking at the recent work by the Relay team (and their close collaboration with the React team) and the React concurrent mode work my impression is that the longer term direction of React is going to be towards all function components, but that is speculative right now.

@tatethurston
Copy link
Author

#2860 doesn't have a "help wanted" or "accepted" label on it - I continue to be unconvinced this is actually a good rule to have. Some things must be class components, and other things are more clear/better as class components; I don't think a lint rule is naunced enough to be appropriate.

That said, I did ask you to open the PR, so I'll give it a good faith review.

I don't have any expectations here, just something to consider. This can always sit or be closed and reopened in the future. I just want things to be code complete (or close) while I have the time.

@ljharb ljharb marked this pull request as draft August 12, 2021 17:40
@rusackas
Copy link

Just wanted to add a voice of support for this approach. prefer-component-style does seem like a reasonable modification to make in the name of flexibility. That said, my/our intended use case is to steer toward function components, and I suspect this will be the case for the majority of codebases, as old class component lifecycle methods become deprecated, and the React community further embraces hooks as a standard. Either way, this seems like a valuable addition to eslint-plugin-react

@nebrius
Copy link

nebrius commented Jun 23, 2022

Just wanted to pop in to say I'd like to have this lint rule too. As pointed out elsewhere, the biggest value here is in enabling consistency across a codebase and the ability to reuse logic between components easily.

In my case, another big use is to use these lint rules to gauge how well we are/are not doing in paying down tech debt over time on an old-ish codebase where we're about 50/50 on function components versus class components. Given the large-ish size of our org and codebase, this has to be automated via tooling so we can create dashboards/reports/etc.

@ljharb
Copy link
Member

ljharb commented Aug 5, 2022

Does the function-component-definition rule meet everyone's use cases here?

@tatethurston
Copy link
Author

tatethurston commented Aug 5, 2022

Does the function-component-definition rule meet everyone's use cases here?

No that rule enforces the function definition and the request here is to enforce function components instead of class components. It’s complementary but none overlapping.

@nebrius
Copy link

nebrius commented Aug 5, 2022

Does the function-component-definition rule meet everyone's use cases here?

No that rule enforces the function definition and the request here is to enforce function components instead of class components. It’s complementary but none overlapping.

Agree with Tate. This rule, while useful, doesn't solve the issue of preventing the introduction of class components into a codebase.

@ljharb ljharb force-pushed the master branch 6 times, most recently from 59af733 to 865ed16 Compare November 11, 2022 02:45
@ljharb ljharb force-pushed the master branch 4 times, most recently from 069314a to 181c68f Compare November 18, 2022 17:19
@ljharb ljharb reopened this Jan 21, 2023
@robwierzbowski
Copy link

Looks like this PR has gone dormant. I also support the addition of this rule. It would be useful when managing a codebase whose contributors have different levels of familiarity with React, e.g., a mix of FE specialists and mid-stack or backend devs that are more used to class-heavy OO languages.

Has anything changed from the maintainer's point of view over the last year? I feel like the greater community has almost fully abandoned classes.

@ljharb
Copy link
Member

ljharb commented Apr 3, 2023

@robwierzbowski nope, nothing's changed there, and ErrorBoundary in particular still is required to be a class component.

@robwierzbowski
Copy link

That's true. I believe this code allows error boundaries to remain class components. As you discussed above, whether this is hardcoded true or an option, the issue could be addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

6 participants