Skip to content

Commit

Permalink
Hook syntax docs
Browse files Browse the repository at this point in the history
Summary: Adding docs for hook syntax

Reviewed By: SamChou19815

Differential Revision: D55537768

fbshipit-source-id: 2aecc9092712d5b941fa1ad6d4272f2f73072188
  • Loading branch information
jbrown215 authored and facebook-github-bot committed Apr 2, 2024
1 parent 99b91d3 commit 388bcf9
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 4 deletions.
8 changes: 6 additions & 2 deletions website/.flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ libs
[options]
all=true

experimental.component_syntax=typing
experimental.component_syntax.deep_read_only=true
experimental.component_syntax=true
experimental.react_rule=deepReadOnlyProps
experimental.react_rule=deepReadOnlyHookReturns
experimental.react_rule=rulesOfHooks
experimental.react_rule=validateRefAccessDuringRender
experimental.component_syntax.hooklike_functions=false
5 changes: 5 additions & 0 deletions website/.flowconfig.snippets
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@
enums=true
all=true
experimental.component_syntax=true
experimental.react_rule=deepReadOnlyProps
experimental.react_rule=deepReadOnlyHookReturns
experimental.react_rule=rulesOfHooks
experimental.react_rule=validateRefAccessDuringRender
experimental.component_syntax.hooklike_functions=false
162 changes: 162 additions & 0 deletions website/docs/react/hook-syntax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
title: Hook Syntax
slug: /react/hook-syntax
---

Hook Syntax is first-class syntax and typechecking support for React hooks, bringing hooks into
the React language as their own entities that are syntactically and semantically distinct from
regular functions, and using Flow to enforce that the [Rules of React](https://react.dev/reference/rules) aren’t violated.

## Basic Usage

The primary difference between writing a function and a hook is the `hook` keyword:
```js flow-check
import {useState, useEffect} from 'react';

hook useOnlineStatus(initial: boolean): boolean {
const [isOnline, setIsOnline] = useState(initial);
useEffect(() => {
// ...
}, []);
return isOnline;
}
```

Hooks can be called just like regular functions:
```js flow-check
import * as React from 'react';

hook useOnlineStatus(): boolean {
return true;
}

component StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
```

Hooks can be exported just like normal functions:

```js flow-check
export hook useNamedExportedHook(): boolean {
return true;
}

export default hook useDefaultExportedHook(): boolean {
return true;
}
```

## Hook Type Annotations
There are a few cases where you might wish to define a value as having the type of a
hook. Because function types and hook types aren’t compatible (more on this below!),
we also introduce a new syntax for hook type annotations, which is simply the
existing function type annotation but preceded by hook.

```js
export const useGKOnlineStatus: hook (boolean) => boolean =
experiment('show_online_status')
? useOnlineStatus
: useAlwaysOnlineStatus
```

## Enforcing the Rules of React with Hook Syntax
With hook syntax, we can now unambiguously distinguish syntactically between hooks and
non-hooks. Flow will use this information to enforce a number of the rules of hooks and
[Rules of React](https://react.dev/reference/rules) generally.

### Preventing Unsafe Mutation
According to the [Rules of React](https://react.dev/reference/rules), refs aren’t allowed
to be read from or written to while a component is rendering, and the return value of
other hooks (especially `useState``) cannot be safely mutated directly at all. By making
Flow aware of hooks as a first-class concept, we can now detect these issues in many cases
and raise errors early, rather than depending on testing to uncover them.

```js flow-check
import {useState, useEffect, useRef} from 'react';
import * as React from 'react';

component MyComponent() {
const ref = useRef<?number>(null);
const [state, setState] = useState<{ val: number }>({val: 0});

state.val = 42; // Flow error: cannot mutate return value of hook

return (
<div>
{ref.current /* Flow error: cannot read ref during rendering */}
</div>
);
}
```
Flow currently prevents component props from being modified within the component.
Hook syntax allows us to extend this checking to hooks, and will let us detect and
raise errors when illegal mutations occur within hook declarations.
```js flow-check
hook useIllegalMutation(values: Array<number>) {
values[0] = 42; // Flow error: mutating argument to hook
// ...
}
```
### Preventing Conditional Hook Calls
[The Rules of Hooks](https://react.dev/reference/rules#rules-of-hooks) prohibit hooks
from being called conditionally. This is covered by [React's ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks),
but now Flow will check for these violations too.
```js flow-check
hook useOnlineStatus(): boolean {
return true;
}

component StatusBar(shouldShowOnlineStatus: boolean) {
if (shouldShowOnlineStatus) {
const onlineStatus = useOnlineStatus();
}

return null;
}
```
### Preventing Conflation of Hooks and Functions
The distinction between hooks and regular functions is reflected in the Flow type system.
Because of the different properties that hooks and functions must obey, it’s Flow error
to pass a value defined as a hook into a position that expects a function type, and
an error to pass a regular JavaScript function into a position that expects a hook.
```js flow-check
import {useState, useEffect} from 'react';

hook useMultiplier(x: number): number {
const [y, setY] = useState(1);
useEffect(() => { setY(0) })
return x * y;
}

component Mapper(args: Array<number>) {
const multArgs = args.map(useMultiplier);

return multArgs;
}
```
In addition, Flow enforces that callees with hook-like names inside hooks and components
are indeed hooks. We also ensure that callees inside of regular function definitions
are never hooks.
```js flow-check
hook useHook() { return null }

function regularJavascript() {
const x = useHook(); // Flow error: cannot call a hook outside of a component or hook
}

component Component() {
const renamedHook = useHook;
renamedHook(); // Flow error: cannot call a hook whose name does not begin with `use`

return null;
}
```
5 changes: 3 additions & 2 deletions website/docs/react/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ React user who already uses Babel. If you need to setup Babel with Flow, you can
follow [this guide](../tools/babel/).

## Check Out Component Syntax
Flow supports a dedicated syntax for writing React components that we recommend instead of using regular
Flow supports a dedicated syntax for writing React components and hooks that we recommend instead of using regular
function/class components. Ensure you are set up using our [most up-to-date instructions to
configure your toolchain](../install) and then take a look at the [Component Syntax](./component-syntax) docs.
configure your toolchain](../install) and then take a look at the [Component Syntax](./component-syntax)
and [Hook Syntax](./hook-syntax) docs.

## React Runtimes

Expand Down
1 change: 1 addition & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ module.exports = {
items: [
'react/index',
'react/component-syntax',
'react/hook-syntax',
'react/function-and-class-components',
'react/events',
'react/refs',
Expand Down

0 comments on commit 388bcf9

Please sign in to comment.