-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Add new eslint rule reference docs #7986
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
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
--- | ||
title: eslint-plugin-react-hooks | ||
--- | ||
|
||
<Intro> | ||
|
||
`eslint-plugin-react-hooks` provides ESLint rules to enforce the [Rules of React](/reference/rules). | ||
|
||
</Intro> | ||
|
||
This plugin helps you catch violations of React's rules at build time, ensuring your components and hooks follow React's rules for correctness and performance. The lints cover both fundamental React patterns (exhaustive-deps and rules-of-hooks) and issues flagged by React Compiler. React Compiler diagnostics are automatically surfaced by this ESLint plugin, and can be used even if your app hasn't adopted the compiler yet. | ||
|
||
<Note> | ||
When the compiler reports a diagnostic, it means that the compiler was able to statically detect a pattern that is not supported or breaks the Rules of React. When it detects this, it **automatically** skips over those components and hooks, while keeping the rest of your app compiled. This ensures optimal coverage of safe optimizations that won't break your app. | ||
|
||
What this means for linting, is that you don’t need to fix all violations immediately. Address them at your own pace to gradually increase the number of optimized components. | ||
</Note> | ||
|
||
## Available Lints {/*available-lints*/} | ||
|
||
* [`component-hook-factories`](/reference/eslint-plugin-react-hooks/lints/component-hook-factories) - Validates against higher order functions defining nested components or hooks | ||
* [`config`](/reference/eslint-plugin-react-hooks/lints/config) - Validates the compiler configuration options | ||
* [`error-boundaries`](/reference/eslint-plugin-react-hooks/lints/error-boundaries) - Validates usage of Error Boundaries instead of try/catch for child errors | ||
* [`exhaustive-deps`](/reference/eslint-plugin-react-hooks/lints/exhaustive-deps) - Validates that dependency arrays for React hooks contain all necessary dependencies | ||
* [`gating`](/reference/eslint-plugin-react-hooks/lints/gating) - Validates configuration of gating mode | ||
* [`globals`](/reference/eslint-plugin-react-hooks/lints/globals) - Validates against assignment/mutation of globals during render | ||
* [`immutability`](/reference/eslint-plugin-react-hooks/lints/immutability) - Validates against mutating props, state, and other immutable values | ||
* [`incompatible-library`](/reference/eslint-plugin-react-hooks/lints/incompatible-library) - Validates against usage of libraries which are incompatible with memoization | ||
* [`preserve-manual-memoization`](/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization) - Validates that existing manual memoization is preserved by the compiler | ||
* [`purity`](/reference/eslint-plugin-react-hooks/lints/purity) - Validates that components/hooks are pure by checking known-impure functions | ||
* [`refs`](/reference/eslint-plugin-react-hooks/lints/refs) - Validates correct usage of refs, not reading/writing during render | ||
* [`rules-of-hooks`](/reference/eslint-plugin-react-hooks/lints/rules-of-hooks) - Validates that components and hooks follow the Rules of Hooks | ||
* [`set-state-in-effect`](/reference/eslint-plugin-react-hooks/lints/set-state-in-effect) - Validates against calling setState synchronously in an effect | ||
* [`set-state-in-render`](/reference/eslint-plugin-react-hooks/lints/set-state-in-render) - Validates against setting state during render | ||
* [`static-components`](/reference/eslint-plugin-react-hooks/lints/static-components) - Validates that components are static, not recreated every render | ||
* [`unsupported-syntax`](/reference/eslint-plugin-react-hooks/lints/unsupported-syntax) - Validates against syntax that React Compiler does not support | ||
* [`use-memo`](/reference/eslint-plugin-react-hooks/lints/use-memo) - Validates usage of the `useMemo` hook without a return value |
102 changes: 102 additions & 0 deletions
102
src/content/reference/eslint-plugin-react-hooks/lints/component-hook-factories.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
--- | ||
title: component-hook-factories | ||
--- | ||
|
||
<Intro> | ||
|
||
Validates against higher order functions defining nested components or hooks. Components and hooks should be defined at the module level. | ||
|
||
</Intro> | ||
|
||
## Rule Details {/*rule-details*/} | ||
|
||
Defining components or hooks inside other functions creates new instances on every call. React treats each as a completely different component, destroying and recreating the entire component tree, losing all state, and causing performance problems. | ||
|
||
### Invalid {/*invalid*/} | ||
|
||
Examples of incorrect code for this rule: | ||
|
||
```js {expectedErrors: {'react-compiler': [14]}} | ||
// ❌ Factory function creating components | ||
function createComponent(defaultValue) { | ||
return function Component() { | ||
// ... | ||
}; | ||
} | ||
|
||
// ❌ Component defined inside component | ||
function Parent() { | ||
function Child() { | ||
// ... | ||
} | ||
|
||
return <Child />; | ||
} | ||
|
||
// ❌ Hook factory function | ||
function createCustomHook(endpoint) { | ||
return function useData() { | ||
// ... | ||
}; | ||
} | ||
``` | ||
|
||
### Valid {/*valid*/} | ||
|
||
Examples of correct code for this rule: | ||
|
||
```js | ||
// ✅ Component defined at module level | ||
function Component({ defaultValue }) { | ||
// ... | ||
} | ||
|
||
// ✅ Custom hook at module level | ||
function useData(endpoint) { | ||
// ... | ||
} | ||
``` | ||
|
||
## Troubleshooting {/*troubleshooting*/} | ||
|
||
### I need dynamic component behavior {/*dynamic-behavior*/} | ||
|
||
You might think you need a factory to create customized components: | ||
|
||
```js | ||
// ❌ Wrong: Factory pattern | ||
function makeButton(color) { | ||
return function Button({children}) { | ||
return ( | ||
<button style={{backgroundColor: color}}> | ||
{children} | ||
</button> | ||
); | ||
}; | ||
} | ||
|
||
const RedButton = makeButton('red'); | ||
const BlueButton = makeButton('blue'); | ||
``` | ||
|
||
Pass [JSX as children](/learn/passing-props-to-a-component#passing-jsx-as-children) instead: | ||
|
||
```js | ||
// ✅ Better: Pass JSX as children | ||
function Button({color, children}) { | ||
return ( | ||
<button style={{backgroundColor: color}}> | ||
{children} | ||
</button> | ||
); | ||
} | ||
|
||
function App() { | ||
return ( | ||
<> | ||
<Button color="red">Red</Button> | ||
<Button color="blue">Blue</Button> | ||
</> | ||
); | ||
} | ||
``` |
90 changes: 90 additions & 0 deletions
90
src/content/reference/eslint-plugin-react-hooks/lints/config.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
--- | ||
title: config | ||
--- | ||
|
||
<Intro> | ||
|
||
Validates the compiler [configuration options](/reference/react-compiler/configuration). | ||
|
||
</Intro> | ||
|
||
## Rule Details {/*rule-details*/} | ||
|
||
React Compiler accepts various [configuration options](/reference/react-compiler/configuration) to control its behavior. This rule validates that your configuration uses correct option names and value types, preventing silent failures from typos or incorrect settings. | ||
|
||
### Invalid {/*invalid*/} | ||
|
||
Examples of incorrect code for this rule: | ||
|
||
```js | ||
// ❌ Unknown option name | ||
module.exports = { | ||
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
compileMode: 'all' // Typo: should be compilationMode | ||
}] | ||
] | ||
}; | ||
|
||
// ❌ Invalid option value | ||
module.exports = { | ||
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
compilationMode: 'everything' // Invalid: use 'all' or 'infer' | ||
}] | ||
] | ||
}; | ||
``` | ||
|
||
### Valid {/*valid*/} | ||
|
||
Examples of correct code for this rule: | ||
|
||
```js | ||
// ✅ Valid compiler configuration | ||
module.exports = { | ||
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
compilationMode: 'infer', | ||
panicThreshold: 'critical_errors' | ||
}] | ||
] | ||
}; | ||
``` | ||
|
||
## Troubleshooting {/*troubleshooting*/} | ||
|
||
### Configuration not working as expected {/*config-not-working*/} | ||
|
||
Your compiler configuration might have typos or incorrect values: | ||
|
||
```js | ||
// ❌ Wrong: Common configuration mistakes | ||
module.exports = { | ||
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
// Typo in option name | ||
compilationMod: 'all', | ||
// Wrong value type | ||
panicThreshold: true, | ||
// Unknown option | ||
optimizationLevel: 'max' | ||
}] | ||
] | ||
}; | ||
``` | ||
|
||
Check the [configuration documentation](/reference/react-compiler/configuration) for valid options: | ||
|
||
```js | ||
// ✅ Better: Valid configuration | ||
module.exports = { | ||
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
compilationMode: 'all', // or 'infer' | ||
panicThreshold: 'none', // or 'critical_errors', 'all_errors' | ||
// Only use documented options | ||
}] | ||
] | ||
}; | ||
``` |
72 changes: 72 additions & 0 deletions
72
src/content/reference/eslint-plugin-react-hooks/lints/error-boundaries.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- | ||
title: error-boundaries | ||
--- | ||
|
||
<Intro> | ||
|
||
Validates usage of Error Boundaries instead of try/catch for errors in child components. | ||
|
||
</Intro> | ||
|
||
## Rule Details {/*rule-details*/} | ||
|
||
Try/catch blocks can't catch errors that happen during React's rendering process. Errors thrown in rendering methods or hooks bubble up through the component tree. Only [Error Boundaries](/reference/react/Component#catching-rendering-errors-with-an-error-boundary) can catch these errors. | ||
|
||
### Invalid {/*invalid*/} | ||
|
||
Examples of incorrect code for this rule: | ||
|
||
```js {expectedErrors: {'react-compiler': [4]}} | ||
// ❌ Try/catch won't catch render errors | ||
function Parent() { | ||
try { | ||
return <ChildComponent />; // If this throws, catch won't help | ||
} catch (error) { | ||
return <div>Error occurred</div>; | ||
} | ||
} | ||
``` | ||
|
||
### Valid {/*valid*/} | ||
|
||
Examples of correct code for this rule: | ||
|
||
```js | ||
// ✅ Using error boundary | ||
function Parent() { | ||
return ( | ||
<ErrorBoundary> | ||
<ChildComponent /> | ||
</ErrorBoundary> | ||
); | ||
} | ||
``` | ||
|
||
## Troubleshooting {/*troubleshooting*/} | ||
|
||
### Why is the linter telling me not to wrap `use` in `try`/`catch`? {/*why-is-the-linter-telling-me-not-to-wrap-use-in-trycatch*/} | ||
|
||
The `use` hook doesn't throw errors in the traditional sense, it suspends component execution. When `use` encounters a pending promise, it suspends the component and lets React show a fallback. Only Suspense and Error Boundaries can handle these cases. The linter warns against `try`/`catch` around `use` to prevent confusion as the `catch` block would never run. | ||
|
||
```js {expectedErrors: {'react-compiler': [5]}} | ||
// ❌ Try/catch around `use` hook | ||
function Component({promise}) { | ||
try { | ||
const data = use(promise); // Won't catch - `use` suspends, not throws | ||
return <div>{data}</div>; | ||
} catch (error) { | ||
return <div>Failed to load</div>; // Unreachable | ||
} | ||
} | ||
|
||
// ✅ Error boundary catches `use` errors | ||
function App() { | ||
return ( | ||
<ErrorBoundary fallback={<div>Failed to load</div>}> | ||
<Suspense fallback={<div>Loading...</div>}> | ||
<DataComponent promise={fetchData()} /> | ||
</Suspense> | ||
</ErrorBoundary> | ||
); | ||
} | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the intention is for the implementation details of
use
to be unnecessary for React developers, but in the current implementation ofuse
it will throw and thecatch
block will run. Someone could log to the console before returning<div>Failed to load</div>
and see thecatch
block did indeed run, couldn't they? While it totally makes sense for developers to not catch or otherwise manipulate whatuse
throws, it seems like the docs statinguse
doesn't throw will cause confusion if and when they encounter it throwing.EDIT: Maybe this is only relevant when using the compiler. I'm looking at it from the perspective of a non-compiled React component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be deprecated soon: facebook/react#34032