Browse Source

Merge branch 'hooks-stable' of https://github.com/gaearon/reactjs.org into gaearon-hooks-stable

main
Brian Vaughn 6 years ago
parent
commit
7d89762407
  1. 206
      content/blog/2019-02-04-react-v16.8.0.md
  2. 194
      content/docs/addons-test-utils.md
  3. 8
      content/docs/hooks-custom.md
  4. 8
      content/docs/hooks-effect.md
  5. 104
      content/docs/hooks-faq.md
  6. 12
      content/docs/hooks-intro.md
  7. 10
      content/docs/hooks-overview.md
  8. 76
      content/docs/hooks-reference.md
  9. 4
      content/docs/hooks-rules.md
  10. 12
      content/docs/hooks-state.md
  11. 2
      content/docs/nav.yml
  12. 17
      content/docs/reference-react.md

206
content/blog/2019-02-04-react-v16.8.0.md

@ -0,0 +1,206 @@
---
title: "React v16.8: The One With Hooks"
author: [gaearon]
---
With React 16.8, [React Hooks](/docs/hooks-intro.html) are available in a stable release!
## What Are Hooks?
Hooks let you use state and other React features without writing a class. You can also **build your own Hooks** to share reusable stateful logic between components.
If you've never heard of Hooks before, you might find these resources interesting:
* [Introducing Hooks](/docs/hooks-intro.html) explains why we're adding Hooks to React.
* [Hooks at a Glance](/docs/hooks-overview.html) is a fast-paced overview of the built-in Hooks.
* [Building Your Own Hooks](/docs/hooks-custom.html) demonstrates code reuse with custom Hooks.
* [Making Sense of React Hooks](https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889) explores the new possibilities unlocked by Hooks.
* [useHooks.com](https://usehooks.com/) showcases community-maintained Hooks recipes and demos.
**You don't have to learn Hooks right now.** Hooks have no breaking changes, and we have no plans to remove classes from React. The [Hooks FAQ](/docs/hooks-faq.html) describes the gradual adoption strategy.
## No Big Rewrites
We don't recommend rewriting your existing applications to use Hooks overnight. Instead, try using Hooks in some of the new components, and let us know what you think. Code using Hooks will work [side by side](/docs/hooks-intro.html#gradual-adoption-strategy) with existing code using classes.
## Can I Use Hooks Today?
Yes! Starting with 16.8.0, React includes a stable implementation of React Hooks for:
* React DOM
* React DOM Server
* React Test Renderer
* React Shallow Renderer
Note that **to enable Hooks, all React packages need to be 16.8.0 or higher**. Hooks won't work if you forget to update, for example, React DOM.
**React Native will support Hooks in the [0.60 release](https://github.com/react-native-community/react-native-releases/issues/79#issuecomment-457735214).**
## Tooling Support
React Hooks are now supported by React DevTools. They are also supported in the latest Flow and TypeScript definitions for React. We strongly recommend enabling a new [lint rule called `eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) to enforce best practices with Hooks. It will soon be included into Create React App by default.
## What's Next
We described our plan for the next months in the recently published [React Roadmap](/blog/2018/11/27/react-16-roadmap.html).
Note that React Hooks don't cover *all* use cases for classes yet but they're [very close](/docs/hooks-faq.html#do-hooks-cover-all-use-cases-for-classes). Currently, only `getSnapshotBeforeUpdate()` and `componentDidCatch()` methods don't have equivalent Hooks APIs, and these lifecycles are relatively uncommon. If you want, you should be able to use Hooks in most of the new code you're writing.
Even while Hooks were in alpha, the React community created many interesting [examples](https://codesandbox.io/react-hooks) and [recipes](https://usehooks.com) using Hooks for animations, forms, subscriptions, integrating with other libraries, and so on. We're excited about Hooks because they make code reuse easier, helping you write your components in a simpler way and make great user experiences. We can't wait to see what you'll create next!
## Testing Hooks
We have added a new API called `ReactTestUtils.act()` in this release. It ensures that the behavior in your tests matches what happens in the browser more closely. We recommend to wrap any code rendering and triggering updates to your components into `act()` calls. Testing libraries like [`react-testing-library`](https://github.com/kentcdodds/react-testing-library) can also wrap their APIs with it.
For example, the counter example from [this page](/docs/hooks-effect.html) can be tested like this:
```js{3,20-22,29-31}
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and componentDidMount
act(() => {
ReactDOM.render(<Counter />, container);
});
const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and componentDidUpdate
act(() => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});
```
The calls to `act()` will also flush the effects inside of them.
If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote.
To reduce the boilerplate, we recommend using [`react-testing-library`](https://git.io/react-testing-library) which is designed to encourage writing tests that use your components as the end users do.
## Thanks
We'd like to thank everybody who commented on the [Hooks RFC](https://github.com/reactjs/rfcs/pull/68) for sharing their feedback. We've read all of your comments and made some adjustments to the final API based on them.
## Installation
### React
React v16.8.0 is available on the npm registry.
To install React 16 with Yarn, run:
```bash
yarn add react@^16.8.0 react-dom@^16.8.0
```
To install React 16 with npm, run:
```bash
npm install --save react@^16.8.0 react-dom@^16.8.0
```
We also provide UMD builds of React via a CDN:
```html
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
```
Refer to the documentation for [detailed installation instructions](/docs/installation.html).
### ESLint Plugin for React Hooks
>Note
>
>As mentioned above, we strongly recommend using the `eslint-plugin-react-hooks` lint rule.
>
>If you're using Create React App, instead of manually configuring ESLint you can wait for the next version of `react-scripts` which will come out shortly and will include this rule.
Assuming you already have ESLint installed, run:
```sh
# npm
npm install eslint-plugin-react-hooks@next --save-dev
# yarn
yarn add eslint-plugin-react-hooks@next --dev
```
Then add it to your ESLint configuration:
```js
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error"
}
}
```
## Changelog
### React
* Add [Hooks](https://reactjs.org/docs/hooks-intro.html) — a way to use state and other React features without writing a class. ([@acdlite](https://github.com/acdlite) et al. in [#13968](https://github.com/facebook/react/pull/13968))
* Improve the `useReducer` Hook lazy initialization API. ([@acdlite](https://github.com/acdlite) in [#14723](https://github.com/facebook/react/pull/14723))
### React DOM
* Bail out of rendering on identical values for `useState` and `useReducer` Hooks. ([@acdlite](https://github.com/acdlite) in [#14569](https://github.com/facebook/react/pull/14569))
* Don’t compare the first argument passed to `useEffect`/`useMemo`/`useCallback` Hooks. ([@acdlite](https://github.com/acdlite) in [#14594](https://github.com/facebook/react/pull/14594))
* Use `Object.is` algorithm for comparing `useState` and `useReducer` values. ([@Jessidhia](https://github.com/Jessidhia) in [#14752](https://github.com/facebook/react/pull/14752))
* Support synchronous thenables passed to `React.lazy()`. ([@gaearon](https://github.com/gaearon) in [#14626](https://github.com/facebook/react/pull/14626))
* Render components with Hooks twice in Strict Mode (DEV-only) to match class behavior. ([@gaearon](https://github.com/gaearon) in [#14654](https://github.com/facebook/react/pull/14654))
* Warn about mismatching Hook order in development. ([@threepointone](https://github.com/threepointone) in [#14585](https://github.com/facebook/react/pull/14585) and [@acdlite](https://github.com/acdlite) in [#14591](https://github.com/facebook/react/pull/14591))
### React Test Renderer
* Support Hooks in the shallow renderer. ([@trueadm](https://github.com/trueadm) in [#14567](https://github.com/facebook/react/pull/14567))
* Fix wrong state in `shouldComponentUpdate` in the presence of `getDerivedStateFromProps` for Shallow Renderer. ([@chenesan](https://github.com/chenesan) in [#14613](https://github.com/facebook/react/pull/14613))
### ESLint Plugin: React Hooks
* Initial [release](https://www.npmjs.com/package/eslint-plugin-react-hooks). ([@calebmer](https://github.com/calebmer) in [#13968](https://github.com/facebook/react/pull/13968))
* Fix reporting after encountering a loop. ([@calebmer](https://github.com/calebmer) and [@Yurickh](https://github.com/Yurickh) in [#14661](https://github.com/facebook/react/pull/14661))
* Don't consider throwing to be a rule violation. ([@sophiebits](https://github.com/sophiebits) in [#14040](https://github.com/facebook/react/pull/14040))
## Hooks Changelog Since Alpha Versions
The above changelog contains all notable changes since our last **stable** release (16.7.0). [As with all our minor releases](/docs/faq-versioning.html), none of the changes break backwards compatibility.
If you're currently using Hooks from an alpha build of React, note that this release does contain some small breaking changes to Hooks. **We don't recommend depending on alphas in production code.** We publish them so we can make changes in response to community feedback before the API is stable.
Here are all breaking changes to Hooks that have been made since the first alpha release:
* Remove `useMutationEffect`. ([@sophiebits](https://github.com/sophiebits) in [#14336](https://github.com/facebook/react/pull/14336))
* Rename `useImperativeMethods` to `useImperativeHandle`. ([@threepointone](https://github.com/threepointone) in [#14565](https://github.com/facebook/react/pull/14565))
* Bail out of rendering on identical values for `useState` and `useReducer` Hooks. ([@acdlite](https://github.com/acdlite) in [#14569](https://github.com/facebook/react/pull/14569))
* Don’t compare the first argument passed to `useEffect`/`useMemo`/`useCallback` Hooks. ([@acdlite](https://github.com/acdlite) in [#14594](https://github.com/facebook/react/pull/14594))
* Use `Object.is` algorithm for comparing `useState` and `useReducer` values. ([@Jessidhia](https://github.com/Jessidhia) in [#14752](https://github.com/facebook/react/pull/14752))
* Render components with Hooks twice in Strict Mode (DEV-only). ([@gaearon](https://github.com/gaearon) in [#14654](https://github.com/facebook/react/pull/14654))
* Improve the `useReducer` Hook lazy initialization API. ([@acdlite](https://github.com/acdlite) in [#14723](https://github.com/facebook/react/pull/14723))

194
content/docs/addons-test-utils.md

@ -19,12 +19,11 @@ var ReactTestUtils = require('react-dom/test-utils'); // ES5 with npm
> Note:
>
> Airbnb has released a testing utility called Enzyme, which makes it easy to assert, manipulate, and traverse your React Components' output. If you're deciding on a unit testing utility to use together with Jest, or any other test runner, it's worth checking out: [http://airbnb.io/enzyme/](http://airbnb.io/enzyme/)
> We recommend using [`react-testing-library`](https://git.io/react-testing-library) which is designed to enable and encourage writing tests that use your components as the end users do.
>
> Alternatively, there is another testing utility called react-testing-library designed to enable and encourage writing tests that use your components as the end users use them. It also works with any test runner: [https://git.io/react-testing-library](https://git.io/react-testing-library)
> Alternatively, Airbnb has released a testing utility called [Enzyme](http://airbnb.io/enzyme/), which makes it easy to assert, manipulate, and traverse your React Components' output.
- [`Simulate`](#simulate)
- [`renderIntoDocument()`](#renderintodocument)
- [`act()`](#act)
- [`mockComponent()`](#mockcomponent)
- [`isElement()`](#iselement)
- [`isElementOfType()`](#iselementoftype)
@ -38,68 +37,88 @@ var ReactTestUtils = require('react-dom/test-utils'); // ES5 with npm
- [`findRenderedDOMComponentWithTag()`](#findrendereddomcomponentwithtag)
- [`scryRenderedComponentsWithType()`](#scryrenderedcomponentswithtype)
- [`findRenderedComponentWithType()`](#findrenderedcomponentwithtype)
- [`renderIntoDocument()`](#renderintodocument)
- [`Simulate`](#simulate)
## Reference
## Shallow Rendering
When writing unit tests for React, shallow rendering can be helpful. Shallow rendering lets you render a component "one level deep" and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.
> Note:
>
> The shallow renderer has moved to `react-test-renderer/shallow`.<br>
> [Learn more about shallow rendering on its reference page.](/docs/shallow-renderer.html)
## Other Utilities
### `Simulate`
```javascript
Simulate.{eventName}(
element,
[eventData]
)
### `act()`
To prepare a component for assertions, wrap the code rendering it and performing updates inside an `act()` call. This makes your test run closer to how React works in the browser.
For example, let's say we have this `Counter` component:
```js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
handleClick() {
this.setState(state => ({
count: state.count + 1,
}));
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.handleClick}>
Click me
</button>
</div>
);
}
}
```
Simulate an event dispatch on a DOM node with optional `eventData` event data.
`Simulate` has a method for [every event that React understands](/docs/events.html#supported-events).
**Clicking an element**
```javascript
// <button ref={(node) => this.button = node}>...</button>
const node = this.button;
ReactTestUtils.Simulate.click(node);
Here is how we can test it:
```js{3,20-22,29-31}
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and componentDidMount
act(() => {
ReactDOM.render(<Counter />, container);
});
const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and componentDidUpdate
act(() => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});
```
**Changing the value of an input field and then pressing ENTER.**
```javascript
// <input ref={(node) => this.textInput = node} />
const node = this.textInput;
node.value = 'giraffe';
ReactTestUtils.Simulate.change(node);
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
```
> Note
>
> You will have to provide any event property that you're using in your component (e.g. keyCode, which, etc...) as React is not creating any of these for you.
* * *
### `renderIntoDocument()`
```javascript
renderIntoDocument(element)
```
Render a React element into a detached DOM node in the document. **This function requires a DOM.**
> Note:
>
> You will need to have `window`, `window.document` and `window.document.createElement` globally available **before** you import `React`. Otherwise React will think it can't access the DOM and methods like `setState` won't work.
Don't forget that dispatching DOM events only works when the DOM container is added to the `document`. You can use a helper like [`react-testing-library`](https://github.com/kentcdodds/react-testing-library) to reduce the boilerplate code.
* * *
@ -265,5 +284,62 @@ findRenderedComponentWithType(
Same as [`scryRenderedComponentsWithType()`](#scryrenderedcomponentswithtype) but expects there to be one result and returns that one result, or throws exception if there is any other number of matches besides one.
***
### `renderIntoDocument()`
```javascript
renderIntoDocument(element)
```
Render a React element into a detached DOM node in the document. **This function requires a DOM.** It is effectively equivalent to:
```js
const domContainer = document.createElement('div');
ReactDOM.render(element, domContainer);
```
> Note:
>
> You will need to have `window`, `window.document` and `window.document.createElement` globally available **before** you import `React`. Otherwise React will think it can't access the DOM and methods like `setState` won't work.
* * *
## Other Utilities
### `Simulate`
```javascript
Simulate.{eventName}(
element,
[eventData]
)
```
Simulate an event dispatch on a DOM node with optional `eventData` event data.
`Simulate` has a method for [every event that React understands](/docs/events.html#supported-events).
**Clicking an element**
```javascript
// <button ref={(node) => this.button = node}>...</button>
const node = this.button;
ReactTestUtils.Simulate.click(node);
```
**Changing the value of an input field and then pressing ENTER.**
```javascript
// <input ref={(node) => this.textInput = node} />
const node = this.textInput;
node.value = 'giraffe';
ReactTestUtils.Simulate.change(node);
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
```
> Note
>
> You will have to provide any event property that you're using in your component (e.g. keyCode, which, etc...) as React is not creating any of these for you.
* * *

8
content/docs/hooks-custom.md

@ -6,14 +6,14 @@ next: hooks-reference.html
prev: hooks-rules.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
Building your own Hooks lets you extract component logic into reusable functions.
When we were learning about [using the Effect Hook](/docs/hooks-effect.html#example-using-hooks-1), we saw this component from a chat application that displays a message indicating whether a friend is online or offline:
```js{4-15}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
@ -39,7 +39,7 @@ function FriendStatus(props) {
Now let's say that our chat application also has a contact list, and we want to render names of online users with a green color. We could copy and paste similar logic above into our `FriendListItem` component but it wouldn't be ideal:
```js{4-15}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function FriendListItem(props) {
const [isOnline, setIsOnline] = useState(null);
@ -74,7 +74,7 @@ When we want to share logic between two JavaScript functions, we extract it to a
**A custom Hook is a JavaScript function whose name starts with "`use`" and that may call other Hooks.** For example, `useFriendStatus` below is our first custom Hook:
```js{3}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);

8
content/docs/hooks-effect.md

@ -6,12 +6,12 @@ next: hooks-rules.html
prev: hooks-intro.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
The *Effect Hook* lets you perform side effects in function components:
```js{1,6-10}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
@ -94,7 +94,7 @@ Now let's see how we can do the same with the `useEffect` Hook.
We've already seen this example at the top of this page, but let's take a closer look at it:
```js{1,6-8}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
@ -199,7 +199,7 @@ Let's see how we could write this component with Hooks.
You might be thinking that we'd need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that `useEffect` is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up:
```js{10-16}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);

104
content/docs/hooks-faq.md

@ -5,7 +5,7 @@ permalink: docs/hooks-faq.html
prev: hooks-reference.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
This page answers some of the frequently asked questions about [Hooks](/docs/hooks-overview.html).
@ -19,7 +19,9 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
-->
* **[Adoption Strategy](#adoption-strategy)**
* [Which versions of React include Hooks?](#which-versions-of-react-include-hooks)
* [Do I need to rewrite all my class components?](#do-i-need-to-rewrite-all-my-class-components)
* [What can I do with Hooks that I couldn't with classes?](#what-can-i-do-with-hooks-that-i-couldnt-with-classes)
* [How much of my React knowledge stays relevant?](#how-much-of-my-react-knowledge-stays-relevant)
* [Should I use Hooks, classes, or a mix of both?](#should-i-use-hooks-classes-or-a-mix-of-both)
* [Do Hooks cover all use cases for classes?](#do-hooks-cover-all-use-cases-for-classes)
@ -35,6 +37,7 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
* [Can I run an effect only on updates?](#can-i-run-an-effect-only-on-updates)
* [How to get the previous props or state?](#how-to-get-the-previous-props-or-state)
* [How do I implement getDerivedStateFromProps?](#how-do-i-implement-getderivedstatefromprops)
* [Is there something like forceUpdate?](#is-there-something-like-forceupdate)
* [Can I make a ref to a function component?](#can-i-make-a-ref-to-a-function-component)
* [What does const [thing, setThing] = useState() mean?](#what-does-const-thing-setthing--usestate-mean)
* **[Performance Optimizations](#performance-optimizations)**
@ -51,10 +54,27 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
## Adoption Strategy
### Which versions of React include Hooks?
Starting with 16.8.0, React includes a stable implementation of React Hooks for:
* React DOM
* React DOM Server
* React Test Renderer
* React Shallow Renderer
Note that **to enable Hooks, all React packages need to be 16.8.0 or higher**. Hooks won't work if you forget to update, for example, React DOM.
React Native will fully support Hooks in its next stable release.
### Do I need to rewrite all my class components?
No. There are [no plans](/docs/hooks-intro.html#gradual-adoption-strategy) to remove classes from React -- we all need to keep shipping products and can't afford rewrites. We recommend trying Hooks in new code.
### What can I do with Hooks that I couldn't with classes?
Hooks offer a powerful and expressive new way to reuse functionality between components. ["Building Your Own Hooks"](/docs/hooks-custom.html) provides a glimpse of what's possible. [This article](https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889) by a React core team member dives deeper into the new capabilities unlocked by Hooks.
### How much of my React knowledge stays relevant?
Hooks are a more direct way to use the React features you already know -- such as state, lifecycle, context, and refs. They don't fundamentally change how React works, and your knowledge of components, props, and top-down data flow is just as relevant.
@ -71,7 +91,7 @@ You can't use Hooks *inside* of a class component, but you can definitely mix cl
Our goal is for Hooks to cover all use cases for classes as soon as possible. There are no Hook equivalents to the uncommon `getSnapshotBeforeUpdate` and `componentDidCatch` lifecycles yet, but we plan to add them soon.
It is a very early time for Hooks, so some integrations like DevTools support or Flow/TypeScript typings may not be ready yet. Some third-party libraries might also not be compatible with Hooks at the moment.
It is an early time for Hooks, and some third-party libraries might not be compatible with Hooks at the moment.
### Do Hooks replace render props and higher-order components?
@ -85,7 +105,7 @@ In the future, new versions of these libraries might also export custom Hooks su
### Do Hooks work with static typing?
Hooks were designed with static typing in mind. Because they're functions, they are easier to type correctly than patterns like higher-order components. We have reached out both to Flow and TypeScript teams in advance, and they plan to include definitions for React Hooks in the future.
Hooks were designed with static typing in mind. Because they're functions, they are easier to type correctly than patterns like higher-order components. The latest Flow and TypeScript React definitions include support for React Hooks.
Importantly, custom Hooks give you the power to constrain React API if you'd like to type them more strictly in some way. React gives you the primitives, but you can combine them in different ways than what we provide out of the box.
@ -93,8 +113,70 @@ Importantly, custom Hooks give you the power to constrain React API if you'd lik
From React's point of view, a component using Hooks is just a regular component. If your testing solution doesn't rely on React internals, testing components with Hooks shouldn't be different from how you normally test components.
For example, let's say we have this counter component:
```js
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
```
We'll test it using React DOM. To make sure that the behavior matches what happens in the browser, we'll wrap the code rendering and updating it into [`ReactTestUtils.act()`](/docs/test-utils.html#act) calls:
```js{3,20-22,29-31}
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and componentDidMount
act(() => {
ReactDOM.render(<Counter />, container);
});
const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and componentDidUpdate
act(() => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});
```
The calls to `act()` will also flush the effects inside of them.
If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote.
To reduce the boilerplate, we recommend using [`react-testing-library`](https://git.io/react-testing-library) which is designed to encourage writing tests that use your components as the end users do.
### What exactly do the [lint rules](https://www.npmjs.com/package/eslint-plugin-react-hooks) enforce?
We provide an [ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) that enforces [rules of Hooks](/docs/hooks-rules.html) to avoid bugs. It assumes that any function starting with "`use`" and a capital letter right after it is a Hook. We recognize this heuristic isn't perfect and there may be some false positives, but without an ecosystem-wide convention there is just no way to make Hooks work well -- and longer names will discourage people from either adopting Hooks or following the convention.
@ -303,6 +385,22 @@ function ScrollView({row}) {
This might look strange at first, but an update during rendering is exactly what `getDerivedStateFromProps` has always been like conceptually.
### Is there something like forceUpdate?
Both `useState` and `useReducer` Hooks [bail out of updates](/docs/hooks-reference.html#bailing-out-of-a-state-update) if the next value is the same as the previous one. Mutating state in place and calling `setState` will not cause a re-render.
Normally, you shouldn't mutate local state in React. However, as an escape hatch, you can use an incrementing counter to force a re-render even if the state has not changed:
```js
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
```
Try to avoid this pattern if possible.
### Can I make a ref to a function component?
While you shouldn't need this often, you may expose some imperative methods to a parent component with the [`useImperativeHandle`](/docs/hooks-reference.html#useimperativehandle) Hook.

12
content/docs/hooks-intro.md

@ -5,10 +5,10 @@ permalink: docs/hooks-intro.html
next: hooks-overview.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
```js{4,5}
import { useState } from 'react';
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
@ -29,6 +29,10 @@ This new function `useState` is the first "Hook" we'll learn about, but this exa
**You can start learning Hooks [on the next page](/docs/hooks-overview.html).** On this page, we'll continue by explaining why we're adding Hooks to React and how they can help you write great applications.
>Note
>
>React 16.8.0 is the first release to support Hooks. When upgrading, don't forget to update all packages, including React DOM. React Native will support Hooks in the next stable release.
## Video Introduction
At React Conf 2018, Sophie Alpert and Dan Abramov introduced Hooks, followed by Ryan Florence demonstrating how to refactor an application to use them. Watch the video here:
@ -99,6 +103,10 @@ Finally, there is no rush to migrate to Hooks. We recommend avoiding any "big re
We intend for Hooks to cover all existing use cases for classes, but **we will keep supporting class components for the foreseeable future.** At Facebook, we have tens of thousands of components written as classes, and we have absolutely no plans to rewrite them. Instead, we are starting to use Hooks in the new code side by side with classes.
## Frequently Asked Questions
We've prepared a [Hooks FAQ page](/docs/hooks-faq.html) that answers the most common questions about Hooks.
## Next Steps
By the end of this page, you should have a rough idea of what problems Hooks are solving, but many details are probably unclear. Don't worry! **Let's now go to [the next page](/docs/hooks-overview.html) where we start learning about Hooks by example.**

10
content/docs/hooks-overview.md

@ -6,7 +6,7 @@ next: hooks-state.html
prev: hooks-intro.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
Hooks are [backwards-compatible](/docs/hooks-intro.html#no-breaking-changes). This page provides an overview of Hooks for experienced React users. This is a fast-paced overview. If you get confused, look for a yellow box like this:
@ -21,7 +21,7 @@ Hooks are [backwards-compatible](/docs/hooks-intro.html#no-breaking-changes). Th
This example renders a counter. When you click the button, it increments the value:
```js{1,4,5}
import { useState } from 'react';
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
@ -77,7 +77,7 @@ The Effect Hook, `useEffect`, adds the ability to perform side effects from a fu
For example, this component sets the document title after React updates the DOM:
```js{1,6-10}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
@ -104,7 +104,7 @@ When you call `useEffect`, you're telling React to run your "effect" function af
Effects may also optionally specify how to "clean up" after them by returning a function. For example, this component uses an effect to subscribe to a friend's online status, and cleans up by unsubscribing from it:
```js{10-16}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
@ -181,7 +181,7 @@ Earlier on this page, we introduced a `FriendStatus` component that calls the `u
First, we'll extract this logic into a custom Hook called `useFriendStatus`:
```js{3}
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);

76
content/docs/hooks-reference.md

@ -6,7 +6,7 @@ prev: hooks-custom.html
next: hooks-faq.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
This page describes the APIs for the built-in Hooks in React.
@ -78,7 +78,7 @@ The "+" and "-" buttons use the functional form, because the updated value is ba
>
> Another option is `useReducer`, which is more suited for managing state objects that contain multiple sub-values.
#### Lazy initialization
#### Lazy initial state
The `initialState` argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render:
@ -89,6 +89,10 @@ const [state, setState] = useState(() => {
});
```
#### Bailing out of a state update
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).)
### `useEffect`
```js
@ -172,11 +176,13 @@ The following Hooks are either variants of the basic ones from the previous sect
### `useReducer`
```js
const [state, dispatch] = useReducer(reducer, initialState);
const [state, dispatch] = useReducer(reducer, initialArg, init);
```
An alternative to [`useState`](#usestate). Accepts a reducer of type `(state, action) => newState`, and returns the current state paired with a `dispatch` method. (If you're familiar with Redux, you already know how this works.)
`useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because [you can pass `dispatch` down instead of callbacks](/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down).
Here's the counter example from the [`useState`](#usestate) section, rewritten to use a reducer:
```js
@ -184,27 +190,20 @@ const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'reset':
return initialState;
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
return state;
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, {count: initialCount});
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'reset'})}>
Reset
</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
@ -212,21 +211,40 @@ function Counter({initialCount}) {
}
```
#### Specifying the initial state
There’s two different ways to initialize `useReducer` state. You may choose either one depending on the use case. The simplest way to pass the initial state as a second argument:
```js{3}
const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
);
```
>Note
>
>React doesn’t use the `state = initialState` argument convention popularized by Redux. The initial value sometimes needs to depend on props and so is specified from the Hook call instead. If you feel strongly about this, you can call `useReducer(reducer, undefined, reducer)` to emulate the Redux behavior, but it's not encouraged.
#### Lazy initialization
`useReducer` accepts an optional third argument, `initialAction`. If provided, the initial action is applied during the initial render. This is useful for computing an initial state that includes values passed via props:
You can also create the initial state lazily. To do this, you can pass an `init` function as the third argument. The initial state will be set to `init(initialArg)`.
```js
const initialState = {count: 0};
It lets you extract the logic for calculating the initial state outside the reducer. This is also handy for resetting the state later in response to an action:
```js{1-3,11-12,21,26}
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'reset':
return {count: action.payload};
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
@ -235,12 +253,7 @@ function reducer(state, action) {
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(
reducer,
initialState,
{type: 'reset', payload: initialCount},
);
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
@ -255,7 +268,9 @@ function Counter({initialCount}) {
}
```
`useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values. It also lets you optimize performance for components that trigger deep updates because [you can pass `dispatch` down instead of callbacks](/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down).
#### Bailing out of a dispatch
If you return the same value from a Reducer Hook as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).)
### `useCallback`
@ -367,7 +382,7 @@ useDebugValue(value)
`useDebugValue` can be used to display a label for custom hooks in React DevTools.
For example, consider the `useFriendStatus` custom hook described in ["Building Your Own Hooks"](/docs/hooks-custom.html):
For example, consider the `useFriendStatus` custom Hook described in ["Building Your Own Hooks"](/docs/hooks-custom.html):
```js{6-8}
function useFriendStatus(friendID) {
@ -375,7 +390,7 @@ function useFriendStatus(friendID) {
// ...
// Show a label in DevTools next to this hook
// Show a label in DevTools next to this Hook
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
@ -385,15 +400,16 @@ function useFriendStatus(friendID) {
> Tip
>
> We don't recommend adding debug values to every custom hook. It's most valuable for custom hooks that are part of shared libraries.
> We don't recommend adding debug values to every custom Hook. It's most valuable for custom Hooks that are part of shared libraries.
#### Defer formatting debug values
In some cases formatting a value for display might be an expensive operation. It's also unnecessary unless a hook is actually inspected.
In some cases formatting a value for display might be an expensive operation. It's also unnecessary unless a Hook is actually inspected.
For this reason `useDebugValue` accepts a formatting function as an optional second parameter. This function is only called if the Hooks are inspected. It receives the debug value as a parameter and should return a formatted display value.
For this reason `useDebugValue` accepts a formatting function as an optional second parameter. This function is only called if the hooks is inspected. It receives the debug value as a parameter and should return a formatted display value.
For example a custom Hook that returned a `Date` value could avoid calling the `toDateString` function unnecessarily by passing the following formatter:
For example a custom hook that returned a `Date` value could avoid calling the `toDateString` function unnecessarily by passing the following formatter:
```js
useDebugValue(date, date => date.toDateString());
```

4
content/docs/hooks-rules.md

@ -6,7 +6,7 @@ next: hooks-custom.html
prev: hooks-effect.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
Hooks are JavaScript functions, but you need to follow two rules when using them. We provide a [linter plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) to enforce these rules automatically:
@ -28,7 +28,7 @@ By following this rule, you ensure that all stateful logic in a component is cle
We released an ESLint plugin called [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) that enforces these two rules. You can add this plugin to your project if you'd like to try it:
```bash
npm install eslint-plugin-react-hooks@next
npm install eslint-plugin-react-hooks
```
```js

12
content/docs/hooks-state.md

@ -6,12 +6,12 @@ next: hooks-effect.html
prev: hooks-overview.html
---
*Hooks* are an upcoming feature that lets you use state and other React features without writing a class. They're currently in React v16.8.0-alpha.1.
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class.
The [previous page](/docs/hooks-intro.html) introduced Hooks with this example:
```js{4-5}
import { useState } from 'react';
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
@ -91,7 +91,7 @@ Hooks **don't** work inside classes. But you can use them instead of writing cla
Our new example starts by importing the `useState` Hook from React:
```js{1}
import { useState } from 'react';
import React, { useState } from 'react';
function Example() {
// ...
@ -123,7 +123,7 @@ class Example extends React.Component {
In a function component, we have no `this`, so we can't assign or read `this.state`. Instead, we call the `useState` Hook directly inside our component:
```js{4,5}
import { useState } from 'react';
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
@ -139,7 +139,7 @@ function Example() {
Now that we know what the `useState` Hook does, our example should make more sense:
```js{4,5}
import { useState } from 'react';
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
@ -196,7 +196,7 @@ Let's now **recap what we learned line by line** and check our understanding.
But if GitHub got away with it for years we can cheat.
-->
```js{1,4,9}
1: import { useState } from 'react';
1: import React, { useState } from 'react';
2:
3: function Example() {
4: const [count, setCount] = useState(0);

2
content/docs/nav.yml

@ -104,7 +104,7 @@
title: JS Environment Requirements
- id: glossary
title: Glossary
- title: Hooks (Preview)
- title: Hooks (New)
isOrdered: true
items:
- id: hooks-intro

17
content/docs/reference-react.md

@ -65,6 +65,23 @@ Suspense lets components "wait" for something before rendering. Today, Suspense
- [`React.lazy`](#reactlazy)
- [`React.Suspense`](#reactsuspense)
### Hooks
*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class. Hooks have a [dedicated docs section](/docs/hooks-intro.html) and a separate API reference:
- [Basic Hooks](/docs/hooks-reference.html#basic-hooks)
- [`useState`](/docs/hooks-reference.html#usestate)
- [`useEffect`](/docs/hooks-reference.html#useeffect)
- [`useContext`](/docs/hooks-reference.html#usecontext)
- [Additional Hooks](/docs/hooks-reference.html#additional-hooks)
- [`useReducer`](/docs/hooks-reference.html#usereducer)
- [`useCallback`](/docs/hooks-reference.html#usecallback)
- [`useMemo`](/docs/hooks-reference.html#usememo)
- [`useRef`](/docs/hooks-reference.html#useref)
- [`useImperativeHandle`](/docs/hooks-reference.html#useimperativehandle)
- [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect)
- [`useDebugValue`](/docs/hooks-reference.html#usedebugvalue)
* * *
## Reference

Loading…
Cancel
Save