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`
|
code: normalizeIndent`
|
||||||
// Valid because they're not matching use[A-Z].
|
// Valid because they're not matching use[A-Z].
|
||||||
fooState();
|
fooState();
|
||||||
use();
|
|
||||||
_use();
|
_use();
|
||||||
_useState();
|
_useState();
|
||||||
use_hook();
|
use_hook();
|
||||||
|
|
@ -496,8 +495,6 @@ const tests = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: normalizeIndent`
|
code: normalizeIndent`
|
||||||
Hook.use();
|
|
||||||
Hook._use();
|
|
||||||
Hook.useState();
|
Hook.useState();
|
||||||
Hook._useState();
|
Hook._useState();
|
||||||
Hook.use42();
|
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 = [
|
||||||
...tests.invalid,
|
...tests.invalid,
|
||||||
|
|
@ -1220,6 +1256,50 @@ if (__EXPERIMENTAL__) {
|
||||||
`,
|
`,
|
||||||
errors: [useEventError('onClick')],
|
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) {
|
function isHookName(s) {
|
||||||
|
if (__EXPERIMENTAL__) {
|
||||||
|
return s === 'use' || /^use[A-Z0-9]/.test(s);
|
||||||
|
}
|
||||||
return /^use[A-Z0-9]/.test(s);
|
return /^use[A-Z0-9]/.test(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +110,13 @@ function isUseEventIdentifier(node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUseIdentifier(node) {
|
||||||
|
if (__EXPERIMENTAL__) {
|
||||||
|
return node.type === 'Identifier' && node.name === 'use';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
meta: {
|
meta: {
|
||||||
type: 'problem',
|
type: 'problem',
|
||||||
|
|
@ -458,7 +468,8 @@ export default {
|
||||||
|
|
||||||
for (const hook of reactHooks) {
|
for (const hook of reactHooks) {
|
||||||
// Report an error if a hook may be called more then once.
|
// 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({
|
context.report({
|
||||||
node: hook,
|
node: hook,
|
||||||
message:
|
message:
|
||||||
|
|
@ -479,7 +490,11 @@ export default {
|
||||||
// path segments.
|
// path segments.
|
||||||
//
|
//
|
||||||
// Special case when we think there might be an early return.
|
// 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 =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" is called ` +
|
`React Hook "${context.getSource(hook)}" is called ` +
|
||||||
'conditionally. React Hooks must be called in the exact ' +
|
'conditionally. React Hooks must be called in the exact ' +
|
||||||
|
|
@ -525,7 +540,8 @@ export default {
|
||||||
// anonymous function expressions. Hopefully this is clarifying
|
// anonymous function expressions. Hopefully this is clarifying
|
||||||
// enough in the common case that the incorrect message in
|
// enough in the common case that the incorrect message in
|
||||||
// uncommon cases doesn't matter.
|
// uncommon cases doesn't matter.
|
||||||
if (isSomewhereInsideComponentOrHook) {
|
// `use(...)` can be called in callbacks.
|
||||||
|
if (isSomewhereInsideComponentOrHook && !isUseIdentifier(hook)) {
|
||||||
const message =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
||||||
'inside a callback. React Hooks must be called in a ' +
|
'inside a callback. React Hooks must be called in a ' +
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user