1 | # TSLint Rules of Hooks
|
2 |
|
3 | [![Downloads badge](https://img.shields.io/npm/dw/tslint-react-hooks.svg?style=flat)](https://www.npmjs.com/package/tslint-react-hooks)
|
4 | [![Version badge](https://img.shields.io/npm/v/tslint-react-hooks.svg?style=flat)](https://www.npmjs.com/package/tslint-react-hooks)
|
5 | [![License badge](https://img.shields.io/npm/l/tslint-react-hooks.svg?style=flat)](https://github.com/Gelio/tslint-react-hooks/blob/master/LICENSE.md)
|
6 | [![GitHub stars badge](https://img.shields.io/github/stars/Gelio/tslint-react-hooks.svg?style=social)](https://github.com/Gelio/tslint-react-hooks)
|
7 | [![Build Status](https://dev.azure.com/vorenygelio/vorenygelio/_apis/build/status/Gelio.tslint-react-hooks?branchName=master)](https://dev.azure.com/vorenygelio/vorenygelio/_build/latest?definitionId=3&branchName=master)
|
8 |
|
9 | ![Demo](https://i.imgur.com/SGHlOvF.png)
|
10 |
|
11 | A TSLint rule that enforces the [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html) for
|
12 | React hooks.
|
13 |
|
14 | The rule is based on an [ESLint plugin for react hooks](https://github.com/facebook/react/blob/master/packages/eslint-plugin-react-hooks/README.md).
|
15 |
|
16 | ## Features
|
17 |
|
18 | - detects using React hooks inside potentially-conditional branches:
|
19 | - if statements
|
20 | - short-circuit conditional expressions (`&&`, `||`)
|
21 | - ternary expressions
|
22 | - loops (`while`, `for`, `do ... while`)
|
23 | - functions that themselves are not custom hooks or components
|
24 | - detects using React hooks in spite of an early return
|
25 |
|
26 | ## Installation
|
27 |
|
28 | First, install [the rule](https://www.npmjs.com/package/tslint-react-hooks):
|
29 |
|
30 | ```sh
|
31 | npm install tslint-react-hooks --save-dev
|
32 | ```
|
33 |
|
34 | Then, enable the rule by modifying `tslint.json`:
|
35 |
|
36 | ```js
|
37 | {
|
38 | "extends": [
|
39 | // your other plugins...
|
40 | "tslint-react-hooks"
|
41 | ],
|
42 | "rules": {
|
43 | // your other rules...
|
44 | "react-hooks-nesting": "error"
|
45 | }
|
46 | }
|
47 | ```
|
48 |
|
49 | To use report rule violations as warnings intead of errors, set it to `"warning"`.
|
50 |
|
51 | ## False positives and not-covered cases
|
52 |
|
53 | There are some cases that seem hard to analyze and may result in false positives or false negatives.
|
54 |
|
55 | In such cases, disable the rule for a specific line using the following comment:
|
56 |
|
57 | ```ts
|
58 | // tslint:disable:react-hooks-nesting
|
59 | useEffect(() => {});
|
60 | ```
|
61 |
|
62 | ### Looping over static arrays
|
63 |
|
64 | The rule may report false positives, for example in:
|
65 |
|
66 | ```ts
|
67 | function MyComponent() {
|
68 | const array = [1, 2, 3];
|
69 |
|
70 | array.forEach(value => {
|
71 | React.useEffect(() => console.log(value));
|
72 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [A hook cannot be used inside of another function]
|
73 | });
|
74 | }
|
75 | ```
|
76 |
|
77 | The `useEffect` hook will be called unconditionally and the call-order will be the same between
|
78 | renders.
|
79 |
|
80 | ### Using renamed hooks (that do not start with _use_)
|
81 |
|
82 | The rule only treats functions that start with _use_ as hooks. Therefore, renaming the hook will
|
83 | result in avoiding the rule:
|
84 |
|
85 | ```ts
|
86 | const renamedUseState = React.useState;
|
87 |
|
88 | function MyComponent() {
|
89 | const [state, setState] = renamedUseState(0);
|
90 | }
|
91 | ```
|
92 |
|
93 | ### Unconditional nesting
|
94 |
|
95 | Unconditional nesting, for example:
|
96 |
|
97 | ```tsx
|
98 | function MyComponent() {
|
99 | if (true) {
|
100 | const variableThatCannotBeLeaked = useContext(SomeContext);
|
101 | useEffect(() => {
|
102 | console.log(variableThatCannotBeLeaked);
|
103 | });
|
104 | }
|
105 |
|
106 | return <div>Text</div>;
|
107 | }
|
108 | ```
|
109 |
|
110 | is treated as conditional nesting. It seems hard to verify if the condition is in fact a constant,
|
111 | therefore such a situation will always result in a rule violation.
|
112 |
|
113 | In situations where such an `if` statement was used to create an additional block scope, use the
|
114 | block scope directly:
|
115 |
|
116 | ```tsx
|
117 | function MyComponent() {
|
118 | {
|
119 | const variableThatCannotBeLeaked = useContext(SomeContext);
|
120 | useEffect(() => {
|
121 | console.log(variableThatCannotBeLeaked);
|
122 | });
|
123 | }
|
124 |
|
125 | return <div>Text</div>;
|
126 | }
|
127 | ```
|
128 |
|
129 | ## Development
|
130 |
|
131 | After pulling the repository, make sure to run
|
132 |
|
133 | ```sh
|
134 | npm install
|
135 | ```
|
136 |
|
137 | The source code for the rule is located in the `src` directory.
|
138 |
|
139 | For more information about the developing custom TSLint rules, take a look at
|
140 | [TSLint's documentation](https://palantir.github.io/tslint/develop/custom-rules/).
|
141 |
|
142 | ### Testing the rule
|
143 |
|
144 | Run
|
145 |
|
146 | ```sh
|
147 | npm run test
|
148 | ```
|
149 |
|
150 | to compile the rule and run automatic TSLint tests.
|
151 |
|
152 | They are located in the `test` directory.
|
153 |
|
154 | ## Author
|
155 |
|
156 | The author of this rule is Grzegorz Rozdzialik.
|