Skip to content

Commit df8a0be

Browse files
authored
chore: Update React Compiler adoption guidelines (#1013)
This file is automatically synced from the `shared-configs` repository. Source: https://github.com/doist/shared-configs/blob/main/
1 parent 293a185 commit df8a0be

File tree

1 file changed

+113
-18
lines changed

1 file changed

+113
-18
lines changed

docs/react-compiler.md

Lines changed: 113 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,24 +93,26 @@ Once all violations in a file are fixed, the file's entry will be removed from `
9393

9494
## Error reference
9595

96-
| Compiler error message | Pattern | Section |
97-
| ----------------------------------------------------------------------------------- | ---------------------------- | ------------------------------------------------ |
98-
| Cannot access refs during render | Ref access during render | [Link](#ref-access-during-render) |
99-
| Existing memoization could not be preserved | Mismatched useMemo deps | [Link](#mismatched-usememo-dependencies) |
100-
| Support destructuring of context variables | Mutating props | [Link](#mutating-props) |
101-
| Expression type `X` cannot be safely reordered | Default parameters for props | [Link](#default-parameters-for-props) |
102-
| Expression type `BinaryExpression` / `LogicalExpression` cannot be safely reordered | switch(true) pattern | [Link](#switchtrue-pattern) |
103-
| Expected Identifier, got `X` key in ObjectExpression | Computed property keys | [Link](#computed-property-keys) |
104-
| Destructure should never be Reassign | Loop variable reassignment | [Link](#loop-variable-reassignment) |
105-
| Hooks must always be called in a consistent order | Conditional hook calls | [Link](#conditional-hook-calls) |
106-
| Cannot access variable before it is declared | Function declaration order | [Link](#function-declaration-order) |
107-
| Handle TryStatement with a finalizer / without a catch clause | Try/catch blocks | [Link](#trycatch-blocks) |
108-
| ThrowStatement inside try/catch not yet supported | Try/catch blocks | [Link](#trycatch-blocks) |
109-
| Support value blocks … within a try/catch statement | Try/catch blocks | [Link](#trycatch-blocks) |
110-
| This value cannot be modified (hook argument) | Mutable objects with useMemo | [Link](#mutable-objects-created-with-usememo) |
111-
| This value cannot be modified (function return) | Mutating return values | [Link](#mutating-function-or-hook-return-values) |
112-
| This value cannot be modified (DOM) | DOM mutations | [Link](#dom-mutations) |
113-
| This value cannot be modified (test code) | Render-time test mutations | [Link](#render-time-mutations-in-test-code) |
96+
| Compiler error message | Pattern | Section |
97+
| ----------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------ |
98+
| Cannot access refs during render | Ref access during render | [Link](#ref-access-during-render) |
99+
| Existing memoization could not be preserved | Mismatched useMemo deps | [Link](#mismatched-usememo-dependencies) |
100+
| Support destructuring of context variables | Mutating props | [Link](#mutating-props) |
101+
| Expression type `X` cannot be safely reordered | Default parameters for props | [Link](#default-parameters-for-props) |
102+
| Expression type `BinaryExpression` / `LogicalExpression` cannot be safely reordered | switch(true) pattern | [Link](#switchtrue-pattern) |
103+
| Expected Identifier, got `X` key in ObjectExpression | Computed property keys | [Link](#computed-property-keys) |
104+
| Destructure should never be Reassign | Loop variable reassignment | [Link](#loop-variable-reassignment) |
105+
| Hooks must always be called in a consistent order | Conditional hook calls | [Link](#conditional-hook-calls) |
106+
| Hooks must be the same function on every render | Hooks passed as props | [Link](#hooks-passed-as-props) |
107+
| Hooks must be called at the top level … not within function expressions | Hooks in function expressions | [Link](#hooks-in-function-expressions) |
108+
| Cannot access variable before it is declared | Function declaration order | [Link](#function-declaration-order) |
109+
| Handle TryStatement with a finalizer / without a catch clause | Try/catch blocks | [Link](#trycatch-blocks) |
110+
| ThrowStatement inside try/catch not yet supported | Try/catch blocks | [Link](#trycatch-blocks) |
111+
| Support value blocks … within a try/catch statement | Try/catch blocks | [Link](#trycatch-blocks) |
112+
| This value cannot be modified (hook argument) | Mutable objects with useMemo | [Link](#mutable-objects-created-with-usememo) |
113+
| This value cannot be modified (function return) | Mutating return values | [Link](#mutating-function-or-hook-return-values) |
114+
| This value cannot be modified (DOM) | DOM mutations | [Link](#dom-mutations) |
115+
| This value cannot be modified (test code) | Render-time test mutations | [Link](#render-time-mutations-in-test-code) |
114116

115117
## Fix patterns
116118

@@ -484,6 +486,99 @@ const currentPlacement = useStoreState(hovercardStore, 'currentPlacement')
484486
const hovercardPlacement = currentPlacement?.split('-')[0]
485487
```
486488

489+
#### Hooks passed as props
490+
491+
> Reason: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
492+
493+
Passing a hook as a prop and calling it inside a child component is a violation — the hook identity can change between renders. Call the hook at the parent level and pass the result instead.
494+
495+
**Before:**
496+
497+
```typescript
498+
function Parent() {
499+
const useValidator = useCallback(
500+
function useValidator() {
501+
return useFormValidator(formId)
502+
},
503+
[formId],
504+
)
505+
return <FormField useValidator={useValidator} />
506+
}
507+
508+
function FormField({ useValidator }: Props) {
509+
const [state, validate] = useValidator() // Violation
510+
// ...
511+
}
512+
```
513+
514+
**After:**
515+
516+
```typescript
517+
function Parent() {
518+
const validator = useFormValidator(formId)
519+
return <FormField validator={validator} />
520+
}
521+
522+
function FormField({ validator }: Props) {
523+
const [state, validate] = validator
524+
// ...
525+
}
526+
```
527+
528+
#### Hooks in function expressions
529+
530+
> Reason: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
531+
532+
Defining a component inside `useMemo` and calling hooks within it is a violationthe compiler sees hooks called inside a non-component function expression. Extract the component to the top level and use Context to pass dynamic values from the parent scope.
533+
534+
**Before:**
535+
536+
```typescript
537+
function useCustomTrigger({ variant }: { variant: 'compact' | 'full' }) {
538+
return useMemo(
539+
() =>
540+
function CustomTrigger(props: TriggerProps) {
541+
const { items } = useListContext() // Violation
542+
return (
543+
<button {...props}>
544+
{variant === 'compact' ? items.length : items.join(', ')}
545+
</button>
546+
)
547+
},
548+
[variant],
549+
)
550+
}
551+
552+
function MySelect({ variant }: Props) {
553+
const CustomTrigger = useCustomTrigger({ variant })
554+
return <Select Trigger={CustomTrigger} />
555+
}
556+
```
557+
558+
**After:**
559+
560+
```typescript
561+
const VariantContext = createContext<'compact' | 'full'>('full')
562+
563+
function CustomTrigger(props: TriggerProps) {
564+
const variant = useContext(VariantContext)
565+
const { items } = useListContext()
566+
return (
567+
<button {...props}>
568+
{variant === 'compact' ? items.length : items.join(', ')}
569+
</button>
570+
)
571+
}
572+
573+
function MySelect({ variant }: Props) {
574+
return (
575+
<VariantContext.Provider value={variant}>
576+
<Select Trigger={CustomTrigger} />
577+
</VariantContext.Provider>
578+
)
579+
}
580+
```
581+
487582
### Function declaration order
488583

489584
> Reason: Cannot access variable before it is declared

0 commit comments

Comments
 (0)