- Start Date: 2018-10-25
- RFC PR: #68
- React Issue: (leave this empty)
In this RFC, we propose introducing Hooks to React.
- Hooks let you reuse logic between components without changing your component hierarchy.
- Hooks let you split one component into smaller functions based on what pieces are related.
- Hooks let you use React without classes.
Hooks are opt-in and 100% backwards-compatible. You can use Hooks side by side with your existing code.
This RFC is very detailed and inconvenient to read as a single Markdown file. Instead of condensing it and sacrificing important details, we decided to write our proposal in the form of documentation, and link to individual pages below. You can consider this documentation to be a part of the RFC, so please feel free to quote and discuss any content from it when commenting on the RFC.
This example renders a counter. When you click the button, it increments the value:
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
This rest of this section is covered by:
This section is covered by:
This section is covered by:
- Using the State Hook
- Using the Effect Hook
- Rules of Hooks
- Writing Custom Hooks
- Hooks API Reference
- Hooks FAQ
Some subtle aspects of this design that we especially appreciate:
- Values returned from one Hook can be passed to another. For example, an effect can read the return value from
useState
because it is automatically in function scope. - Custom Hooks allow abstracting logic without any special involvement from React.
- One variable per
useState
call instead of a singlethis.state
object means fewer object property accesses that are hard for VMs to optimize. - State variables names (with
useState
) can be automatically minified. - No changes to build tooling are required.
A non-exhaustive list of drawbacks of this Hooks design follows.
- Introducing a new way to write components means more to learn and means confusion while both classes and functions are used.
- The “Rules of Hooks”: in order to make Hooks work, React requires that Hooks are called unconditionally. Component authors may find it unintuitive that Hook calls can't be moved inside
if
statements, loops, and helper functions. - The “Rules of Hooks” can make some types of refactoring more difficult. For example, adding an early return to a component is no longer possible without moving all Hook calls to before that conditional.
- Event handlers need to be recreated on each render in order to reference the latest copy of props and state, which reduces the effectiveness of
PureComponent
andReact.memo
. - It's possible for closures (like the ones passed to
useEffect
anduseCallback
) to capture old versions of props and state values. In particular, this happens if the “inputs” array is inadvertently missing one of captured variables. This can be confusing. - React relies on internal global state in order to determine which component is currently rendering when each Hook is called. This is “less pure” and may be unintuitive.
React.memo
(as a replacement forshouldComponentUpdate
) only has access to the old and new props; there's no easy way to skip rerendering for an inconsequential state update.useState
uses a tuple return value that requires typing the same name twice to declare a state field (likeconst [rhinoceros, setRhinoceros] = useState(null);
), which may be cumbersome for long names.useState
uses the overloaded type() => T | T
to support lazy initialization. But when storing a function in state (that is, whenT
is a function type) you must always use a lazy initializeruseState(() => myFunction)
because the types are indistinguishable at runtime. Similarly, the functional updater form must be used when setting state to a new function value.
Possible alternatives follow. In our opinion, none of these cleanly solve all the problems that Hooks do.
- The status quo: supporting state and effects only in class components, with no changes.
- Alternative designs that allow using state and effects without classes:
- Language-level syntax akin to
state foo = 5;
in the style of DisplayScript. - Built-in render prop components that provide stateful features, like Reactions Component.
- Built-in higher-order components that pass stateful features as arguments.
- Other “stateful function components” ideas laid out in react-future.
- Language-level syntax akin to
- Alternative designs that facilitate sharing of stateful logic:
- Syntax sugar for render props, like an
adopt
keyword or Epitath. - Reintroducing mixins in class components.
- Syntax sugar for render props, like an
We're planning to recommend a gradual adoption strategy for Hooks. Hooks don't break existing patterns, and they can be used side by side with class components. It's possible that codemod could be written to automatically convert simple class components to use Hooks instead, but none is currently planned.
The documentation linked above is our best attempt at teaching this. Over time we expect that we'll integrate the Hooks documentation with the main concept docs instead of having it in a separate section.
Hooks synthesize ideas from several different sources:
- Our old experiments with functional APIs in the react-future repository.
- React community's experiments with render prop APIs, including Ryan Florence's Reactions Component.
- Dominic Gannaway's
adopt
keyword proposal as a sugar syntax for render props. - State variables and state cells in DisplayScript.
- Reducer components in ReasonReact.
- Subscriptions in Rx.
- Algebraic effects in Multicore OCaml.
Sebastian Markbåge came up with the original design for Hooks, later refined by Andrew Clark, Sophie Alpert, Dominic Gannaway, and other members of the React team.