mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Add RulesOfHooks support for use
Usage of the new `use` hook needs to conform to the rules of hooks, with
the one exception that it can be called conditionally.
ghstack-source-id: 7ea5beceaf
Pull Request resolved: https://github.com/facebook/react/pull/25370
This commit is contained in:
parent
338e6a967c
commit
3fd9bd8e74
|
|
@ -257,7 +257,6 @@ const tests = {
|
|||
code: normalizeIndent`
|
||||
// Valid because they're not matching use[A-Z].
|
||||
fooState();
|
||||
use();
|
||||
_use();
|
||||
_useState();
|
||||
use_hook();
|
||||
|
|
@ -496,8 +495,6 @@ const tests = {
|
|||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
Hook.use();
|
||||
Hook._use();
|
||||
Hook.useState();
|
||||
Hook._useState();
|
||||
Hook.use42();
|
||||
|
|
@ -1146,6 +1143,45 @@ if (__EXPERIMENTAL__) {
|
|||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function App() {
|
||||
const text = use(Promise.resolve('A'));
|
||||
return <Text text={text} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function App() {
|
||||
if (shouldShowText) {
|
||||
const text = use(query);
|
||||
return <Text text={text} />
|
||||
}
|
||||
return <Text text={shouldFetchBackupText ? use(backupQuery) : "Nothing to see here"} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function App() {
|
||||
let data = [];
|
||||
for (const query of queries) {
|
||||
const text = use(item);
|
||||
data.push(text);
|
||||
}
|
||||
return <Child data={data} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function App() {
|
||||
const data = someCallback((x) => use(x));
|
||||
return <Child data={data} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
tests.invalid = [
|
||||
...tests.invalid,
|
||||
|
|
@ -1220,6 +1256,50 @@ if (__EXPERIMENTAL__) {
|
|||
`,
|
||||
errors: [useEventError('onClick')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
Hook.use();
|
||||
Hook._use();
|
||||
Hook.useState();
|
||||
Hook._useState();
|
||||
Hook.use42();
|
||||
Hook.useHook();
|
||||
Hook.use_hook();
|
||||
`,
|
||||
errors: [
|
||||
topLevelError('Hook.use'),
|
||||
topLevelError('Hook.useState'),
|
||||
topLevelError('Hook.use42'),
|
||||
topLevelError('Hook.useHook'),
|
||||
],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function notAComponent() {
|
||||
use(promise);
|
||||
}
|
||||
`,
|
||||
errors: [functionError('use', 'notAComponent')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
const text = use(promise);
|
||||
function App() {
|
||||
return <Text text={text} />
|
||||
}
|
||||
`,
|
||||
errors: [topLevelError('use')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
class C {
|
||||
m() {
|
||||
use(promise);
|
||||
}
|
||||
}
|
||||
`,
|
||||
errors: [classError('use')],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
*/
|
||||
|
||||
function isHookName(s) {
|
||||
if (__EXPERIMENTAL__) {
|
||||
return s === 'use' || /^use[A-Z0-9]/.test(s);
|
||||
}
|
||||
return /^use[A-Z0-9]/.test(s);
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +110,13 @@ function isUseEventIdentifier(node) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function isUseIdentifier(node) {
|
||||
if (__EXPERIMENTAL__) {
|
||||
return node.type === 'Identifier' && node.name === 'use';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
|
|
@ -458,7 +468,8 @@ export default {
|
|||
|
||||
for (const hook of reactHooks) {
|
||||
// Report an error if a hook may be called more then once.
|
||||
if (cycled) {
|
||||
// `use(...)` can be called in loops.
|
||||
if (cycled && !isUseIdentifier(hook)) {
|
||||
context.report({
|
||||
node: hook,
|
||||
message:
|
||||
|
|
@ -479,7 +490,11 @@ export default {
|
|||
// path segments.
|
||||
//
|
||||
// Special case when we think there might be an early return.
|
||||
if (!cycled && pathsFromStartToEnd !== allPathsFromStartToEnd) {
|
||||
if (
|
||||
!cycled &&
|
||||
pathsFromStartToEnd !== allPathsFromStartToEnd &&
|
||||
!isUseIdentifier(hook) // `use(...)` can be called conditionally.
|
||||
) {
|
||||
const message =
|
||||
`React Hook "${context.getSource(hook)}" is called ` +
|
||||
'conditionally. React Hooks must be called in the exact ' +
|
||||
|
|
@ -525,7 +540,8 @@ export default {
|
|||
// anonymous function expressions. Hopefully this is clarifying
|
||||
// enough in the common case that the incorrect message in
|
||||
// uncommon cases doesn't matter.
|
||||
if (isSomewhereInsideComponentOrHook) {
|
||||
// `use(...)` can be called in callbacks.
|
||||
if (isSomewhereInsideComponentOrHook && !isUseIdentifier(hook)) {
|
||||
const message =
|
||||
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
||||
'inside a callback. React Hooks must be called in a ' +
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user