• Stars
    star
    217
  • Rank 182,446 (Top 4 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

TSLint rule for detecting invalid uses of React Hooks

TSLint Rules of Hooks

Downloads badge Version badge License badge GitHub stars badge Build Status

Demo

A TSLint rule that enforces the Rules of Hooks for React hooks.

The rule is based on an ESLint plugin for react hooks.

Features

  • detects using React hooks inside potentially-conditional branches:
    • if statements
    • short-circuit conditional expressions (&&, ||)
    • ternary expressions
    • loops (while, for, do ... while)
    • functions that themselves are not custom hooks or components
  • detects using React hooks in spite of an early return
  • support for detecting hooks from namespaces other than React (e.g. MyHooks.useHook) (optional)

Installation

First, install the rule:

npm install tslint-react-hooks --save-dev

Then, enable the rule by modifying tslint.json:

{
  "extends": [
    // your other plugins...
    "tslint-react-hooks"
  ],
  "rules": {
    // your other rules...
    "react-hooks-nesting": "error"
  }
}

To use report rule violations as warnings intead of errors, set it to "warning".

Options

While the rule works fine out-of-the-box, it can be customized. To specify options, use the following syntax when modifying tslint.json:

{
  "extends": [
    // your other plugins...
    "tslint-react-hooks"
  ],
  "rules": {
    // your other rules...
    "react-hooks-nesting": {
      "severity": "error", // "error", "warning", "default" or "off"
      "options": {
        // options go here
      }
    }
  }
}

Available options

  • "detect-hooks-from-non-react-namespace" - when set to true, violations will be also reported hooks accessed from sources other than the React namespace (e.g. MyHooks.useHook will be treated as a hook).

    By default, only direct calls (e.g. useHook) or calls from React namespace (e.g. React.useState) are treated as hooks.

Have an idea for an option? Create a new issue.

Workarounds

For some arrow functions/function expressions, the rule has no way to determine whether those are a component, a hook, both of which could contain hook calls, or a regular function that should not contain hook calls.

const withHoc = <TProps extends object>(Component: ComponentType<TProps>) => (
  props: TProps,
) => {
  const [state] = useState();
  return <Component {...props} />;
};

The workaround in those cases is to use a named function expression:

const withHoc = <TProps extends object>(Component: ComponentType<TProps>) =>
  function WrappedComponent(props: TProps) {
    const [state] = useState();
    return <Component {...props} />;
  };

Naming the function like a component (in PascalCase) unambiguously lets the rule treat the function as a component.

False positives and not-covered cases

There are some cases that seem hard to analyze and may result in false positives or false negatives.

In such cases, disable the rule for a specific line using the following comment:

// tslint:disable:react-hooks-nesting
useEffect(() => {});

Looping over static arrays

The rule may report false positives, for example in:

function MyComponent() {
  const array = [1, 2, 3];

  array.forEach(value => {
    React.useEffect(() => console.log(value));
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [A hook cannot be used inside of another function]
  });
}

The useEffect hook will be called unconditionally and the call-order will be the same between renders.

Using renamed hooks (that do not start with use)

The rule only treats functions that start with use as hooks. Therefore, renaming the hook will result in avoiding the rule:

const renamedUseState = React.useState;

function MyComponent() {
  const [state, setState] = renamedUseState(0);
}

Unconditional nesting

Unconditional nesting, for example:

function MyComponent() {
  if (true) {
    const variableThatCannotBeLeaked = useContext(SomeContext);
    useEffect(() => {
      console.log(variableThatCannotBeLeaked);
    });
  }

  return <div>Text</div>;
}

is treated as conditional nesting. It seems hard to verify if the condition is in fact a constant, therefore such a situation will always result in a rule violation.

In situations where such an if statement was used to create an additional block scope, use the block scope directly:

function MyComponent() {
  {
    const variableThatCannotBeLeaked = useContext(SomeContext);
    useEffect(() => {
      console.log(variableThatCannotBeLeaked);
    });
  }

  return <div>Text</div>;
}

Development

After pulling the repository, make sure to run

npm install

The source code for the rule is located in the src directory.

For more information about the developing custom TSLint rules, take a look at TSLint's documentation.

Testing the rule

Run

npm run test

to compile the rule and run automatic TSLint tests.

They are located in the test directory.

Author

The author of this rule is Grzegorz Rozdzialik.

More Repositories

1

go-global-update

A command to update globally installed go executables
Go
128
star
2

cmp-natdat

nvim-cmp source to autocomplete natural dates and turm them into ISO dates
Lua
23
star
3

loose-ts-check

Run TS type-check and ignore certain errors in some files
TypeScript
18
star
4

tslint-import-group-ordering

TSLint rule for ordering import groups
TypeScript
17
star
5

ubuntu-dotfiles

Personal dotfiles and installation files for Ubuntu
Lua
7
star
6

aisd2-lab-tasks

Laboratory tasks for the AiSD 2 class at the MiNI Faculty at WUT
C#
4
star
7

mdx-deck-template

MDX deck template with some useful components
JavaScript
4
star
8

taio-graph-visualization

Graph visualization for a university project
TypeScript
4
star
9

bir-regon

Polish public company data query API
JavaScript
3
star
10

websocket-image-stream

Example websocket server and client
JavaScript
3
star
11

vscode-temporary-vim-motions

VSCode extension providing a popup for vim motions
TypeScript
3
star
12

next-intercept-ssr-navigation

Next app showcasing instant client-side navigation for pages with getServerSideProps
TypeScript
2
star
13

node-image-stream

Node server using socket.io to stream (possibly) images to users
JavaScript
2
star
14

js-testing-workshop

Workshop on Javascript & React testing
JavaScript
2
star
15

url-poller

Polls URL looking for updates and notifies upon them
JavaScript
2
star
16

p3-lab1617

College programming assignments (C++ and C#) (semester 2016/2017)
C#
2
star
17

wpf-image-browser

Image Browser created in WPF for the Windows Programming class
C#
2
star
18

the-project-game

Multi agent environment simulation
TypeScript
1
star
19

pixijs-spiral-transition

Slideshow transition using PixiJS
TypeScript
1
star
20

find-quadruples-with-product

A solution to an algorithmic challenge
Rust
1
star
21

polygon-clipper

TypeScript
1
star
22

react-native-todo-app

TODO app in React Native using Native Base
JavaScript
1
star
23

websocket-performance-demo

Demo of pushing lots of data via WebSocket
JavaScript
1
star
24

nvim-relative-date

Display relative dates as inline virtual text
Lua
1
star
25

CAL-web

A web modeler for CAL models
Java
1
star
26

doodle-parser

Parses Doodle polls and returns the results
TypeScript
1
star
27

react-hooks-workshop

Workshop on React Hooks
JavaScript
1
star
28

state-synchronizers

Deterministically update state based on other state
TypeScript
1
star
29

wedding-seater

Plan seating wedding guests at tables
TypeScript
1
star
30

distributed-computing-webworker-poc

Proof of concept presenting distributed computing using WebWorkers with C# code
WebAssembly
1
star