Skip to content

Commit

Permalink
fixup! [New] Symmetric useState hook variable names
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanbeevers committed Dec 12, 2021
1 parent f66510b commit d032842
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 77 deletions.
113 changes: 52 additions & 61 deletions lib/rules/hook-use-state.js
Expand Up @@ -15,6 +15,7 @@ const report = require('../util/report');

const messages = {
useStateErrorMessage: 'useState call is not destructured into value + setter pair',

};

module.exports = {
Expand Down Expand Up @@ -136,75 +137,65 @@ module.exports = {
&& variableNodes.length === 2;

if (!isSymmetricGetterSetterPair) {
const suggestions = [
{
desc: 'Destructure useState call into value + setter pair',
fix: (fixer) => {
const fix = fixer.replaceTextRange(
node.parent.id.range,
`[${valueVariableName}, ${expectedSetterVariableName}]`
);

return fix;
},
},
];

const isSingleGetter = valueVariable && variableNodes.length === 1;
const isUseStateCalledWithSingleArgument = node.arguments.length === 1;
if (isSingleGetter && isUseStateCalledWithSingleArgument) {
const useMemoReactImportSpecifier = namedReactImports ? namedReactImports.find((specifier) => specifier.imported.name === 'useMemo') : undefined;
const sourceCode = context.getSourceCode();
const useStateArgumentSourceCode = sourceCode.getText(node.arguments[0]);

report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{
node: node.parent.id,
suggest: [
{
desc: 'Replace useState call with useMemo',
fix: (fixer) => {
const useMemoImportName = useMemoReactImportSpecifier && useMemoReactImportSpecifier.local.name;

const useMemoReference = useMemoImportName
|| (defaultReactImportName
&& `${defaultReactImportName}.useMemo`)
|| 'useMemo';

const fixes = [
// Add useMemo import, if necessary
useStateReactImportSpecifier
&& (!useMemoReactImportSpecifier || defaultReactImportName)
&& fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'),
// Convert single-value destructure to simple assignment
fixer.replaceTextRange(node.parent.id.range, valueVariableName),
// Convert useState call to useMemo + arrow function + dependency array
fixer.replaceTextRange(
node.range,
`${useMemoReference}(() => ${useStateArgumentSourceCode}, [])`
),
].filter(Boolean);

return fixes;
},
},
{
desc: 'Destructure useState call into value + setter pair',
fix: (fixer) => {
const fix = fixer.replaceTextRange(
node.parent.id.range,
`[${valueVariableName}, ${expectedSetterVariableName}]`
);

return fix;
},
},
].filter(Boolean),
}
);
} else {
report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{
node: node.parent.id,
fix: valueVariableName ? (fixer) => fixer.replaceTextRange(
[node.parent.id.range[0], node.parent.id.range[1]],
`[${valueVariableName}, ${expectedSetterVariableName}]`
) : undefined,
}
);
suggestions.unshift({
desc: 'Replace useState call with useMemo',
fix: (fixer) => {
const useMemoImportName = useMemoReactImportSpecifier && useMemoReactImportSpecifier.local.name;

const useMemoReference = useMemoImportName
|| (defaultReactImportName
&& `${defaultReactImportName}.useMemo`)
|| 'useMemo';

const fixes = [
// Add useMemo import, if necessary
useStateReactImportSpecifier
&& (!useMemoReactImportSpecifier || defaultReactImportName)
&& fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'),
// Convert single-value destructure to simple assignment
fixer.replaceTextRange(node.parent.id.range, valueVariableName),
// Convert useState call to useMemo + arrow function + dependency array
fixer.replaceTextRange(
node.range,
`${useMemoReference}(() => ${useStateArgumentSourceCode}, [])`
),
].filter(Boolean);

return fixes;
},
});
}

report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{
node: node.parent.id,
suggest: suggestions,
}
);
}
},
})),
Expand Down
50 changes: 34 additions & 16 deletions tests/lib/rules/hook-use-state.js
Expand Up @@ -230,11 +230,15 @@ const tests = {
}`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [
{
output: `import { useState } from 'react'
export default function useColor() {
const [color, setColor] = useState()
}`,
},
],
}],
},
{
code: `import { useState } from 'react'
Expand Down Expand Up @@ -319,12 +323,14 @@ const tests = {
}`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [{
output: `import { useState } from 'react'
export default function useColor() {
const [color, setColor] = useState()
return [color]
}`,
}],
}],
},
{
code: `import { useState } from 'react'
Expand All @@ -334,12 +340,14 @@ const tests = {
}`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [{
output: `import { useState } from 'react'
export default function useColor() {
const [color, setColor] = useState()
return [color, setColor]
}`,
}],
}],
},
{
code: `import { useState } from 'react'
Expand All @@ -353,27 +361,33 @@ const tests = {
const [color, setFlavor, extraneous] = useState()`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [{
output: `import { useState } from 'react'
const [color, setColor] = useState()`,
}],
}],
},
{
code: `import { useState } from 'react'
const [color, setFlavor] = useState()`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [{
output: `import { useState } from 'react'
const [color, setColor] = useState()`,
}],
}],
},
{
code: `import { useState } from 'react'
const [color, setFlavor] = useState<string>()`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [{
output: `import { useState } from 'react'
const [color, setColor] = useState<string>()`,
}],
}],
features: ['ts', 'no-babel-old'],
},
{
Expand All @@ -384,12 +398,14 @@ const tests = {
}`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import { useState } from 'react'
suggestions: [{
output: `import { useState } from 'react'
export default function useColor() {
const [color, setColor] = useState<string>('#ffffff')
return [color, setFlavor]
}`,
}],
}],
features: ['ts', 'no-babel-old'],
},
{
Expand All @@ -400,12 +416,14 @@ const tests = {
}`,
errors: [{
message: 'useState call is not destructured into value + setter pair',
}],
output: `import React from 'react'
suggestions: [{
output: `import React from 'react'
export default function useColor() {
const [color, setColor] = React.useState<string>('#ffffff')
return [color, setFlavor]
}`,
}],
}],
features: ['ts', 'no-babel-old'],
},
]),
Expand Down

0 comments on commit d032842

Please sign in to comment.