Skip to content

Commit ace7be5

Browse files
authored
Add React and Hook config for ESLint (vercel#270)
* feat: install and config react rules for eslint * fix: add names to components in example * fix: move use of hook before conditional return * chore: bump plugins versions + recommended rules + fixes
1 parent 6461c44 commit ace7be5

File tree

8 files changed

+335
-85
lines changed

8 files changed

+335
-85
lines changed

.eslintrc

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,44 @@
11
{
2-
"parser": "@typescript-eslint/parser",
3-
"parserOptions": {
4-
"ecmaVersion": 8,
5-
"sourceType": "module",
6-
"ecmaFeatures": {
7-
"impliedStrict": true,
8-
"experimentalObjectRestSpread": true
9-
},
10-
"allowImportExportEverywhere": true
11-
},
12-
"plugins": [
13-
"@typescript-eslint"
14-
],
15-
"extends": [
16-
"eslint:recommended",
17-
"plugin:@typescript-eslint/eslint-recommended",
18-
"plugin:@typescript-eslint/recommended",
19-
"prettier",
20-
"prettier/@typescript-eslint"
21-
],
22-
"env": {
23-
"es6": true,
24-
"browser": true,
25-
"node": true,
26-
"jest": true
27-
},
28-
"rules": {
29-
"func-names": [
30-
"error",
31-
"as-needed"
32-
],
33-
"no-shadow": "error",
34-
"prefer-const": 0,
35-
"@typescript-eslint/explicit-function-return-type": 0,
36-
"@typescript-eslint/no-use-before-define": 0,
37-
"@typescript-eslint/camelcase": 0,
38-
"@typescript-eslint/no-var-requires": 0,
39-
"@typescript-eslint/no-explicit-any": 0
40-
}
2+
"parser": "@typescript-eslint/parser",
3+
"parserOptions": {
4+
"ecmaVersion": 8,
5+
"sourceType": "module",
6+
"ecmaFeatures": {
7+
"impliedStrict": true,
8+
"experimentalObjectRestSpread": true
9+
},
10+
"allowImportExportEverywhere": true
11+
},
12+
"plugins": ["@typescript-eslint", "react-hooks"],
13+
"extends": [
14+
"eslint:recommended",
15+
"plugin:react/recommended",
16+
"plugin:@typescript-eslint/eslint-recommended",
17+
"plugin:@typescript-eslint/recommended",
18+
"prettier",
19+
"prettier/@typescript-eslint"
20+
],
21+
"settings": {
22+
"react": {
23+
"version": "detect"
24+
}
25+
},
26+
"env": {
27+
"es6": true,
28+
"browser": true,
29+
"node": true,
30+
"jest": true
31+
},
32+
"rules": {
33+
"func-names": ["error", "as-needed"],
34+
"no-shadow": "error",
35+
"prefer-const": 0,
36+
"@typescript-eslint/explicit-function-return-type": 0,
37+
"@typescript-eslint/no-use-before-define": 0,
38+
"@typescript-eslint/camelcase": 0,
39+
"@typescript-eslint/no-var-requires": 0,
40+
"@typescript-eslint/no-explicit-any": 0,
41+
"react-hooks/rules-of-hooks": "error",
42+
"react-hooks/exhaustive-deps": "warn"
43+
}
4144
}

examples/axios-typescript/libs/useRequest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default function useRequest<Data = unknown, Error = unknown>(
4040
initialData: initialData && {
4141
status: 200,
4242
statusText: 'InitialData',
43+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4344
config: request!,
4445
headers: {},
4546
data: initialData

examples/basic-typescript/pages/[user]/[repo].tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fetch from '../../libs/fetch'
44

55
import useSWR from 'swr'
66

7-
export default () => {
7+
function Repo() {
88
const id =
99
typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
1010
const { data } = useSWR<{
@@ -33,3 +33,5 @@ export default () => {
3333
</div>
3434
)
3535
}
36+
37+
export default Repo

examples/basic-typescript/pages/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fetch from '../libs/fetch'
44

55
import useSWR from 'swr'
66

7-
export default () => {
7+
function HomePage() {
88
const { data } = useSWR<string[]>('/api/data', fetch)
99

1010
return (
@@ -24,3 +24,5 @@ export default () => {
2424
</div>
2525
)
2626
}
27+
28+
export default HomePage

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
"@zeit/ncc": "0.20.5",
4747
"eslint": "6.6.0",
4848
"eslint-config-prettier": "6.5.0",
49+
"eslint-plugin-react": "7.20.6",
50+
"eslint-plugin-react-hooks": "4.1.2",
4951
"husky": "2.4.1",
5052
"jest": "24.9.0",
5153
"lint-staged": "8.2.1",

src/use-swr.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,43 @@ function useSWR<Data = any, Error = any>(
650650
revalidate
651651
])
652652

653+
// define returned state
654+
// can be memorized since the state is a ref
655+
const memoizedState = useMemo(() => {
656+
const state = { revalidate, mutate: boundMutate } as responseInterface<
657+
Data,
658+
Error
659+
>
660+
Object.defineProperties(state, {
661+
error: {
662+
// `key` might be changed in the upcoming hook re-render,
663+
// but the previous state will stay
664+
// so we need to match the latest key and data (fallback to `initialData`)
665+
get: function() {
666+
stateDependencies.current.error = true
667+
return keyRef.current === key ? stateRef.current.error : initialError
668+
},
669+
enumerable: true
670+
},
671+
data: {
672+
get: function() {
673+
stateDependencies.current.data = true
674+
return keyRef.current === key ? stateRef.current.data : initialData
675+
},
676+
enumerable: true
677+
},
678+
isValidating: {
679+
get: function() {
680+
stateDependencies.current.isValidating = true
681+
return stateRef.current.isValidating
682+
},
683+
enumerable: true
684+
}
685+
})
686+
687+
return state
688+
}, [revalidate])
689+
653690
// suspense
654691
if (config.suspense) {
655692
// in suspense mode, we can't return empty state
@@ -705,42 +742,7 @@ function useSWR<Data = any, Error = any>(
705742
}
706743
}
707744

708-
// define returned state
709-
// can be memorized since the state is a ref
710-
return useMemo(() => {
711-
const state = { revalidate, mutate: boundMutate } as responseInterface<
712-
Data,
713-
Error
714-
>
715-
Object.defineProperties(state, {
716-
error: {
717-
// `key` might be changed in the upcoming hook re-render,
718-
// but the previous state will stay
719-
// so we need to match the latest key and data (fallback to `initialData`)
720-
get: function() {
721-
stateDependencies.current.error = true
722-
return keyRef.current === key ? stateRef.current.error : initialError
723-
},
724-
enumerable: true
725-
},
726-
data: {
727-
get: function() {
728-
stateDependencies.current.data = true
729-
return keyRef.current === key ? stateRef.current.data : initialData
730-
},
731-
enumerable: true
732-
},
733-
isValidating: {
734-
get: function() {
735-
stateDependencies.current.isValidating = true
736-
return stateRef.current.isValidating
737-
},
738-
enumerable: true
739-
}
740-
})
741-
742-
return state
743-
}, [revalidate])
745+
return memoizedState
744746
}
745747

746748
const SWRConfig = SWRConfigContext.Provider

test/use-swr.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,7 +2117,7 @@ describe('useSWR - config callbacks', () => {
21172117
let state = null
21182118
let count = 0
21192119

2120-
function Page(props) {
2120+
function Page(props: { text: string }) {
21212121
const { data, revalidate } = useSWR(
21222122
'config callbacks - onSuccess',
21232123
() => new Promise(res => setTimeout(() => res(count++), 200)),
@@ -2166,7 +2166,7 @@ describe('useSWR - config callbacks', () => {
21662166
let state = null
21672167
let count = 0
21682168

2169-
function Page(props) {
2169+
function Page(props: { text: string }) {
21702170
const { data, revalidate, error } = useSWR(
21712171
'config callbacks - onError',
21722172
() =>
@@ -2218,7 +2218,7 @@ describe('useSWR - config callbacks', () => {
22182218
it('should trigger the onErrorRetry event with the latest version of the onErrorRetry callback', async () => {
22192219
let state = null
22202220
let count = 0
2221-
function Page(props) {
2221+
function Page(props: { text: string }) {
22222222
const { data, error } = useSWR(
22232223
'config callbacks - onErrorRetry',
22242224
() =>
@@ -2271,7 +2271,7 @@ describe('useSWR - config callbacks', () => {
22712271
let state = null
22722272
let count = 0
22732273

2274-
function Page(props) {
2274+
function Page(props: { text: string }) {
22752275
const { data } = useSWR(
22762276
'config callbacks - onLoadingSlow',
22772277
() => new Promise(res => setTimeout(() => res(count++), 200)),

0 commit comments

Comments
 (0)