@@ -28,6 +28,8 @@ module.exports = {
2828 fixable : 'code' ,
2929 messages,
3030 schema : [ ] ,
31+ type : 'suggestion' ,
32+ hasSuggestions : true ,
3133 } ,
3234
3335 create : Components . detect ( ( context , components ) => ( {
@@ -134,18 +136,75 @@ module.exports = {
134136 && variableNodes . length === 2 ;
135137
136138 if ( ! isSymmetricGetterSetterPair ) {
137- report (
138- context ,
139- messages . useStateErrorMessage ,
140- 'useStateErrorMessage' ,
141- {
142- node : node . parent . id ,
143- fix : valueVariableName ? ( fixer ) => fixer . replaceTextRange (
144- [ node . parent . id . range [ 0 ] , node . parent . id . range [ 1 ] ] ,
145- `[${ valueVariableName } , ${ expectedSetterVariableName } ]`
146- ) : undefined ,
147- }
148- ) ;
139+ const isSingleGetter = valueVariable && variableNodes . length === 1 ;
140+ const isUseStateCalledWithSingleArgument = node . arguments . length === 1 ;
141+ if ( isSingleGetter && isUseStateCalledWithSingleArgument ) {
142+ const useMemoReactImportSpecifier = namedReactImports ? namedReactImports . find ( ( specifier ) => specifier . imported . name === 'useMemo' ) : undefined ;
143+ const sourceCode = context . getSourceCode ( ) ;
144+ const useStateArgumentSourceCode = sourceCode . getText ( node . arguments [ 0 ] ) ;
145+
146+ report (
147+ context ,
148+ messages . useStateErrorMessage ,
149+ 'useStateErrorMessage' ,
150+ {
151+ node : node . parent . id ,
152+ suggest : [
153+ {
154+ desc : 'Replace useState call with useMemo' ,
155+ fix : ( fixer ) => {
156+ const useMemoImportName = useMemoReactImportSpecifier && useMemoReactImportSpecifier . local . name ;
157+
158+ const useMemoReference = useMemoImportName
159+ || ( defaultReactImportName
160+ && `${ defaultReactImportName } .useMemo` )
161+ || 'useMemo' ;
162+
163+ const fixes = [
164+ // Add useMemo import, if necessary
165+ useStateReactImportSpecifier
166+ && ( ! useMemoReactImportSpecifier || defaultReactImportName )
167+ && fixer . insertTextAfter ( useStateReactImportSpecifier , ', useMemo' ) ,
168+ // Convert single-value destructure to simple assignment
169+ fixer . replaceTextRange ( node . parent . id . range , valueVariableName ) ,
170+ // Convert useState call to useMemo + arrow function + dependency array
171+ fixer . replaceTextRange (
172+ node . range ,
173+ `${ useMemoReference } (() => ${ useStateArgumentSourceCode } , [])`
174+ ) ,
175+ ] . filter ( Boolean ) ;
176+
177+ return fixes ;
178+ } ,
179+ } ,
180+ {
181+ desc : 'Destructure useState call into value + setter pair' ,
182+ fix : ( fixer ) => {
183+ const fix = fixer . replaceTextRange (
184+ node . parent . id . range ,
185+ `[${ valueVariableName } , ${ expectedSetterVariableName } ]`
186+ ) ;
187+
188+ return fix ;
189+ } ,
190+ } ,
191+ ] . filter ( Boolean ) ,
192+ }
193+ ) ;
194+ } else {
195+ report (
196+ context ,
197+ messages . useStateErrorMessage ,
198+ 'useStateErrorMessage' ,
199+ {
200+ node : node . parent . id ,
201+ fix : valueVariableName ? ( fixer ) => fixer . replaceTextRange (
202+ [ node . parent . id . range [ 0 ] , node . parent . id . range [ 1 ] ] ,
203+ `[${ valueVariableName } , ${ expectedSetterVariableName } ]`
204+ ) : undefined ,
205+ }
206+ ) ;
207+ }
149208 }
150209 } ,
151210 } ) ) ,
0 commit comments