diff --git a/.flowconfig b/.flowconfig index 836f6ec1eb0..06dfa2f32cb 100644 --- a/.flowconfig +++ b/.flowconfig @@ -17,7 +17,6 @@ module.system.node.resolve_dirname=src esproposal.class_static_fields=enable esproposal.class_instance_fields=enable -unsafe.enable_getters_and_setters=true munge_underscores=false @@ -32,4 +31,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError [version] -^0.56.0 +^0.87.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e10f4f53e4a..e5ae1e67358 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,32 +56,6 @@ In particular, you should prefer named `function` declarations over `const myFun #### Don't use features that aren't standardized yet. -For example, **don't** write this: - -```js -class MyComponent extends React.Component { - state = {value: ''}; - handleChange = (e) => { - this.setState({value: e.target.value}); - }; -} -``` - -Instead, **do** write this: - -```js -class MyComponent extends React.Component { - constructor(props) { - super(props); - this.handleChange = this.handleChange.bind(this); - this.state = {value: ''}; - } - handleChange(e) { - this.setState({value: e.target.value}); - } -} -``` - Ignore this rule if you're specifically describing an experimental proposal. Make sure to mention its experimental nature in the code and in the surrounding text. ### Style diff --git a/content/docs/accessibility.md b/content/docs/accessibility.md index 6e75e34e6f3..f31818e5695 100644 --- a/content/docs/accessibility.md +++ b/content/docs/accessibility.md @@ -169,39 +169,35 @@ To set focus in React, we can use [Refs to DOM elements](/docs/refs-and-the-dom. Using this, we first create a ref to an element in the JSX of a component class: -```javascript{4-5,8-9,13} -class CustomTextInput extends React.Component { - constructor(props) { - super(props); - // Create a ref to store the textInput DOM element - this.textInput = React.createRef(); - } - render() { +```javascript{2-3,5-6,10} +function CustomTextInput() { + // Create a ref to store the textInput DOM element + const textInput = useRef(); + // Use the `ref` callback to store a reference to the text input DOM - // element in an instance field (for example, this.textInput). - return ( - - ); - } + // element in a ref field (for example, textInput). + return ( + + ); } ``` Then we can focus it elsewhere in our component when needed: ```javascript - focus() { +function focus() { // Explicitly focus the text input using the raw DOM API // Note: we're accessing "current" to get the DOM node - this.textInput.current.focus(); + textInput.current.focus(); } ``` Sometimes a parent component needs to set focus to an element in a child component. We can do this by [exposing DOM refs to parent components](/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components) through a special prop on the child component that forwards the parent's ref to the child's DOM node. -```javascript{4,12,16} +```javascript{4,10,13} function CustomTextInput(props) { return (
@@ -210,20 +206,16 @@ function CustomTextInput(props) { ); } -class Parent extends React.Component { - constructor(props) { - super(props); - this.inputElement = React.createRef(); - } - render() { - return ( - - ); - } +function Parent() { + const inputElement = useRef(); + + return ( + + ); } // Now you can set focus when required. -this.inputElement.current.focus(); +inputElement.current.focus(); ``` When using a HOC to extend components, it is recommended to [forward the ref](/docs/forwarding-refs.html) to the wrapped component using the `forwardRef` function of React. If a third party HOC does not implement ref forwarding, the above pattern can still be used as a fallback. @@ -246,52 +238,40 @@ To illustrate this, let's look at a prolific example of broken accessibility cau This is typically implemented by attaching a `click` event to the `window` object that closes the popover: -```javascript{12-14,26-30} -class OuterClickExample extends React.Component { - constructor(props) { - super(props); - - this.state = { isOpen: false }; - this.toggleContainer = React.createRef(); - - this.onClickHandler = this.onClickHandler.bind(this); - this.onClickOutsideHandler = this.onClickOutsideHandler.bind(this); - } +```javascript{4-10,16-20} +function OuterClickExample() { + const [isOpen, setIsOpen] = useState(false); - componentDidMount() { - window.addEventListener('click', this.onClickOutsideHandler); - } + useEffect(() => { + window.addEventListener('click', onClickOutsideHandler); - componentWillUnmount() { - window.removeEventListener('click', this.onClickOutsideHandler); - } + return () => { + window.removeEventListener('click', onClickOutsideHandler); + }; + }, []); - onClickHandler() { - this.setState(currentState => ({ - isOpen: !currentState.isOpen - })); + function onClickHandler() { + setIsOpen(isOpen => !isOpen); } - onClickOutsideHandler(event) { - if (this.state.isOpen && !this.toggleContainer.current.contains(event.target)) { - this.setState({ isOpen: false }); + function onClickOutsideHandler(event) { + if (!toggleContainer.current.contains(event.target)) { + setIsOpen(false); } } - render() { - return ( -
- - {this.state.isOpen && ( -
    -
  • Option 1
  • -
  • Option 2
  • -
  • Option 3
  • -
- )} -
- ); - } + return ( +
+ + {isOpen && ( +
    +
  • Option 1
  • +
  • Option 2
  • +
  • Option 3
  • +
+ )} +
+ ); } ``` @@ -301,63 +281,49 @@ This may work fine for users with pointer devices, such as a mouse, but operatin The same functionality can be achieved by using appropriate event handlers instead, such as `onBlur` and `onFocus`: -```javascript{19-29,31-34,37-38,40-41} -class BlurExample extends React.Component { - constructor(props) { - super(props); - - this.state = { isOpen: false }; - this.timeOutId = null; +```javascript{9-17,19-22,24-25,27-28} +function BlurExample() { + const [isOpen, setIsOpen] = useState(false); + const timeOutId = useRef(); - this.onClickHandler = this.onClickHandler.bind(this); - this.onBlurHandler = this.onBlurHandler.bind(this); - this.onFocusHandler = this.onFocusHandler.bind(this); - } - - onClickHandler() { - this.setState(currentState => ({ - isOpen: !currentState.isOpen - })); + function onClickHandler() { + setIsOpen(isOpen => !isOpen); } // We close the popover on the next tick by using setTimeout. // This is necessary because we need to first check if // another child of the element has received focus as // the blur event fires prior to the new focus event. - onBlurHandler() { - this.timeOutId = setTimeout(() => { - this.setState({ - isOpen: false - }); + function onBlurHandler() { + timeOutId.current = setTimeout(() => { + setIsOpen(false); }); } // If a child receives focus, do not close the popover. - onFocusHandler() { - clearTimeout(this.timeOutId); + function onFocusHandler() { + clearTimeout(timeOutId.current); } - render() { - // React assists us by bubbling the blur and - // focus events to the parent. - return ( -
- - {this.state.isOpen && ( -
    -
  • Option 1
  • -
  • Option 2
  • -
  • Option 3
  • -
- )} -
- ); - } + // React assists us by bubbling the blur and + // focus events to the parent. + return ( +
+ + {isOpen && ( +
    +
  • Option 1
  • +
  • Option 2
  • +
  • Option 3
  • +
+ )} +
+ ); } ``` diff --git a/content/docs/add-react-to-a-website.md b/content/docs/add-react-to-a-website.md index b103cfae4ef..f9f1acab7a6 100644 --- a/content/docs/add-react-to-a-website.md +++ b/content/docs/add-react-to-a-website.md @@ -131,7 +131,7 @@ const e = React.createElement; // Display a "Like" ); diff --git a/content/docs/codebase-overview.md b/content/docs/codebase-overview.md index 091aff3aa0c..1c574e7e240 100644 --- a/content/docs/codebase-overview.md +++ b/content/docs/codebase-overview.md @@ -4,7 +4,7 @@ title: Codebase Overview layout: contributing permalink: docs/codebase-overview.html prev: how-to-contribute.html -next: implementation-notes.html +next: design-principles.html redirect_from: - "contributing/codebase-overview.html" --- @@ -189,16 +189,12 @@ The only other officially supported renderer is [`react-art`](https://github.com ### Reconcilers {#reconcilers} -Even vastly different renderers like React DOM and React Native need to share a lot of logic. In particular, the [reconciliation](/docs/reconciliation.html) algorithm should be as similar as possible so that declarative rendering, custom components, state, lifecycle methods, and refs work consistently across platforms. +Even vastly different renderers like React DOM and React Native need to share a lot of logic. In particular, the [reconciliation](/docs/reconciliation.html) algorithm should be as similar as possible so that declarative rendering, custom components, state, hooks, and refs work consistently across platforms. -To solve this, different renderers share some code between them. We call this part of React a "reconciler". When an update such as `setState()` is scheduled, the reconciler calls `render()` on components in the tree and mounts, updates, or unmounts them. +To solve this, different renderers share some code between them. We call this part of React a "reconciler". When an update such as `setState()` is scheduled, the reconciler calls components in the tree and mounts, updates, or unmounts them. Reconcilers are not packaged separately because they currently have no public API. Instead, they are exclusively used by renderers such as React DOM and React Native. -### Stack Reconciler {#stack-reconciler} - -The "stack" reconciler is the implementation powering React 15 and earlier. We have since stopped using it, but it is documented in detail in the [next section](/docs/implementation-notes.html). - ### Fiber Reconciler {#fiber-reconciler} The "fiber" reconciler is a new effort aiming to resolve the problems inherent in the stack reconciler and fix a few long-standing issues. It has been the default reconciler since React 16. @@ -208,7 +204,7 @@ Its main goals are: * Ability to split interruptible work in chunks. * Ability to prioritize, rebase and reuse work in progress. * Ability to yield back and forth between parents and children to support layout in React. -* Ability to return multiple elements from `render()`. +* Ability to return multiple elements from components. * Better support for error boundaries. You can read more about React Fiber Architecture [here](https://github.com/acdlite/react-fiber-architecture) and [here](https://blog.ag-grid.com/inside-fiber-an-in-depth-overview-of-the-new-reconciliation-algorithm-in-react). While it has shipped with React 16, the async features are not enabled by default yet. @@ -221,6 +217,6 @@ React implements a synthetic event system which is agnostic of the renderers and There is a [video with a deep code dive into it](https://www.youtube.com/watch?v=dRo_egw7tBc) (66 mins). -### What Next? {#what-next} +### Next Steps {#next-steps} -Read the [next section](/docs/implementation-notes.html) to learn about the pre-React 16 implementation of reconciler in more detail. We haven't documented the internals of the new reconciler yet. +Read the [next section](/docs/design-principles.html) to learn about the guiding principles we use for React development. diff --git a/content/docs/components-and-props.md b/content/docs/components-and-props.md index 4b3bbb6e608..4ea0efdb2dc 100644 --- a/content/docs/components-and-props.md +++ b/content/docs/components-and-props.md @@ -13,14 +13,14 @@ redirect_from: - "tips/props-in-getInitialState-as-anti-pattern.html" - "tips/communicate-between-components.html" prev: rendering-elements.html -next: state-and-lifecycle.html +next: state.html --- Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. You can find a [detailed component API reference here](/docs/react-component.html). Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called "props") and return React elements describing what should appear on the screen. -## Function and Class Components {#function-and-class-components} +## Function Components {#function-components} The simplest way to define a component is to write a JavaScript function: @@ -32,20 +32,6 @@ function Welcome(props) { This function is a valid React component because it accepts a single "props" (which stands for properties) object argument with data and returns a React element. We call such components "function components" because they are literally JavaScript functions. -You can also use an [ES6 class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) to define a component: - -```js -class Welcome extends React.Component { - render() { - return

Hello, {this.props.name}

; - } -} -``` - -The above two components are equivalent from React's point of view. - -Classes have some additional features that we will discuss in the [next sections](/docs/state-and-lifecycle.html). Until then, we will use function components for their conciseness. - ## Rendering a Component {#rendering-a-component} Previously, we only encountered React elements that represent DOM tags: @@ -259,4 +245,4 @@ React is pretty flexible but it has a single strict rule: **All React components must act like pure functions with respect to their props.** -Of course, application UIs are dynamic and change over time. In the [next section](/docs/state-and-lifecycle.html), we will introduce a new concept of "state". State allows React components to change their output over time in response to user actions, network responses, and anything else, without violating this rule. +Of course, application UIs are dynamic and change over time. In the [next section](/docs/state.html), we will introduce a new concept of "state". State allows React components to change their output over time in response to user actions, network responses, and anything else, without violating this rule. diff --git a/content/docs/composition-vs-inheritance.md b/content/docs/composition.md similarity index 73% rename from content/docs/composition-vs-inheritance.md rename to content/docs/composition.md index c86735ef7de..8da146ecd3e 100644 --- a/content/docs/composition-vs-inheritance.md +++ b/content/docs/composition.md @@ -1,16 +1,17 @@ --- -id: composition-vs-inheritance -title: Composition vs Inheritance -permalink: docs/composition-vs-inheritance.html +id: composition +title: Composition +permalink: docs/composition.html redirect_from: - "docs/multiple-components.html" + - "docs/composition-vs-inheritence.html" prev: lifting-state-up.html next: thinking-in-react.html --- -React has a powerful composition model, and we recommend using composition instead of inheritance to reuse code between components. +React has a powerful composition model, and we recommend using it to reuse code between components. -In this section, we will consider a few problems where developers new to React often reach for inheritance, and show how we can solve them with composition. +In this section, we will consider a few problems and show how we can solve them with composition. ## Containment {#containment} @@ -113,9 +114,9 @@ function WelcomeDialog() { [**Try it on CodePen**](https://codepen.io/gaearon/pen/kkEaOZ?editors=0010) -Composition works equally well for components defined as classes: +Composition works equally well for components with state: -```js{10,27-31} +```js{10,21-25} function Dialog(props) { return ( @@ -130,43 +131,34 @@ function Dialog(props) { ); } -class SignUpDialog extends React.Component { - constructor(props) { - super(props); - this.handleChange = this.handleChange.bind(this); - this.handleSignUp = this.handleSignUp.bind(this); - this.state = {login: ''}; - } +function SignUpDialog() { + const [login, setLogin] = useState(''); - render() { - return ( - - - - - ); - } + return ( + + + + + ); handleChange(e) { - this.setState({login: e.target.value}); + setLogin(e.target.value); } handleSignUp() { - alert(`Welcome aboard, ${this.state.login}!`); + alert(`Welcome aboard, ${login}!`); } } ``` [**Try it on CodePen**](https://codepen.io/gaearon/pen/gwZbYa?editors=0010) -## So What About Inheritance? {#so-what-about-inheritance} - -At Facebook, we use React in thousands of components, and we haven't found any use cases where we would recommend creating component inheritance hierarchies. +## Conclusion {#conclusion} Props and composition give you all the flexibility you need to customize a component's look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions. -If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module. The components may import it and use that function, object, or a class, without extending it. +If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module. The components may import it and use that function, object, or a class without having to render each other. diff --git a/content/docs/conditional-rendering.md b/content/docs/conditional-rendering.md index 7df19bb98cf..a4de00beab7 100644 --- a/content/docs/conditional-rendering.md +++ b/content/docs/conditional-rendering.md @@ -70,44 +70,36 @@ function LogoutButton(props) { } ``` -In the example below, we will create a [stateful component](/docs/state-and-lifecycle.html#adding-local-state-to-a-class) called `LoginControl`. +In the example below, we will create a [stateful component](/docs/state.html#adding-local-state-to-a-class) called `LoginControl`. It will render either `` or `` depending on its current state. It will also render a `` from the previous example: -```javascript{20-25,29,30} -class LoginControl extends React.Component { - constructor(props) { - super(props); - this.handleLoginClick = this.handleLoginClick.bind(this); - this.handleLogoutClick = this.handleLogoutClick.bind(this); - this.state = {isLoggedIn: false}; - } +```javascript{13-18,22,23} +function LoginControl(props) { + const [isLoggedIn, setIsLoggedIn] = setState(false); - handleLoginClick() { - this.setState({isLoggedIn: true}); + functino handleLoginClick() { + setIsLoggedIn(true); } - handleLogoutClick() { - this.setState({isLoggedIn: false}); + function handleLogoutClick() { + setIsLoggedIn(false) } - render() { - const isLoggedIn = this.state.isLoggedIn; - let button; - - if (isLoggedIn) { - button = ; - } else { - button = ; - } - - return ( -
- - {button} -
- ); + let button; + + if (isLoggedIn) { + button = ; + } else { + button = ; } + + return ( +
+ + {button} +
+ ); } ReactDOM.render( @@ -158,32 +150,26 @@ Another method for conditionally rendering elements inline is to use the JavaScr In the example below, we use it to conditionally render a small block of text. -```javascript{5} -render() { - const isLoggedIn = this.state.isLoggedIn; - return ( -
- The user is {isLoggedIn ? 'currently' : 'not'} logged in. -
- ); -} +```javascript{3} +return ( +
+ The user is {isLoggedIn ? 'currently' : 'not'} logged in. +
+); ``` It can also be used for larger expressions although it is less obvious what's going on: -```js{5,7,9} -render() { - const isLoggedIn = this.state.isLoggedIn; - return ( -
- {isLoggedIn ? ( - - ) : ( - - )} -
- ); -} +```js{3,5,7} +return ( +
+ {isLoggedIn ? ( + + ) : ( + + )} +
+); ``` Just like in JavaScript, it is up to you to choose an appropriate style based on what you and your team consider more readable. Also remember that whenever conditions become too complex, it might be a good time to [extract a component](/docs/components-and-props.html#extracting-components). @@ -194,7 +180,7 @@ In rare cases you might want a component to hide itself even though it was rende In the example below, the `` is rendered depending on the value of the prop called `warn`. If the value of the prop is `false`, then the component does not render: -```javascript{2-4,29} +```javascript{2-4,22} function WarningBanner(props) { if (!props.warn) { return null; @@ -207,29 +193,21 @@ function WarningBanner(props) { ); } -class Page extends React.Component { - constructor(props) { - super(props); - this.state = {showWarning: true}; - this.handleToggleClick = this.handleToggleClick.bind(this); - } +function Page() { + const [showWarning, setShowWarning] = useState(true); - handleToggleClick() { - this.setState(state => ({ - showWarning: !state.showWarning - })); - } - - render() { - return ( -
- - -
- ); + function handleToggleClick() { + setShowWarning(showWarning => !showWarning; } + + return ( +
+ + +
+ ); } ReactDOM.render( @@ -240,4 +218,4 @@ ReactDOM.render( [**Try it on CodePen**](https://codepen.io/gaearon/pen/Xjoqwm?editors=0010) -Returning `null` from a component's `render` method does not affect the firing of the component's lifecycle methods. For instance `componentDidUpdate` will still be called. +Returning `null` from a component does not affect the firing of the component's hooks. For instance `useEffect` will still be called. diff --git a/content/docs/context.md b/content/docs/context.md index 5b4592c2fcb..83486981d14 100644 --- a/content/docs/context.md +++ b/content/docs/context.md @@ -130,7 +130,7 @@ Every Context object comes with a Provider React component that allows consuming Accepts a `value` prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree. -All consumers that are descendants of a Provider will re-render whenever the Provider's `value` prop changes. The propagation from Provider to its descendant consumers is not subject to the `shouldComponentUpdate` method, so the consumer is updated even when an ancestor component bails out of the update. +All consumers that are descendants of a Provider will re-render whenever the Provider's `value` prop changes. Changes are determined by comparing the new and old values using the same algorithm as [`Object.is`](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description). @@ -138,49 +138,30 @@ Changes are determined by comparing the new and old values using the same algori > > The way changes are determined can cause some issues when passing objects as `value`: see [Caveats](#caveats). -### `Class.contextType` {#classcontexttype} +### `React.useContext` {#reactusecontext} ```js -class MyClass extends React.Component { - componentDidMount() { - let value = this.context; - /* perform a side-effect at mount using the value of MyContext */ - } - componentDidUpdate() { - let value = this.context; - /* ... */ - } - componentWillUnmount() { - let value = this.context; - /* ... */ - } - render() { - let value = this.context; - /* render something based on the value of MyContext */ - } -} -MyClass.contextType = MyContext; -``` +function MyComponent { + const value = React.useContext(MyContext); -The `contextType` property on a class can be assigned a Context object created by [`React.createContext()`](#reactcreatecontext). This lets you consume the nearest current value of that Context type using `this.context`. You can reference this in any of the lifecycle methods including the render function. + useEffect(() => { + /* perform a side-effect at mount using the value of MyContext */ -> Note: -> -> You can only subscribe to a single context using this API. If you need to read more than one see [Consuming Multiple Contexts](#consuming-multiple-contexts). -> -> If you are using the experimental [public class fields syntax](https://babeljs.io/docs/plugins/transform-class-properties/), you can use a **static** class field to initialize your `contextType`. + return () => { + /* ... */ + }; + }, []); + useEffect(() => { + /* ... */ + }); -```js -class MyClass extends React.Component { - static contextType = MyContext; - render() { - let value = this.context; - /* render something based on the value */ - } + /* render something based on the value of MyContext */ } ``` +The `useContext` hook can be called with a Context object created by [`React.createContext()`](#reactcreatecontext). This lets you consume the nearest current value of that Context type using its return value. + ### `Context.Consumer` {#contextconsumer} ```js @@ -189,7 +170,7 @@ class MyClass extends React.Component { ``` -A React component that subscribes to context changes. This lets you subscribe to a context within a [function component](/docs/components-and-props.html#function-and-class-components). +A React component that subscribes to context changes. This lets you subscribe to a context within JSX. Requires a [function as a child](/docs/render-props.html#using-props-other-than-render). The function receives the current context value and returns a React node. The `value` argument passed to the function will be equal to the `value` prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the `value` argument will be equal to the `defaultValue` that was passed to `createContext()`. @@ -241,12 +222,8 @@ It is often necessary to update the context from a component that is nested some ### Consuming Multiple Contexts {#consuming-multiple-contexts} -To keep context re-rendering fast, React needs to make each context consumer a separate node in the tree. - `embed:context/multiple-contexts.js` -If two or more context values are often used together, you might want to consider creating your own render prop component that provides both. - ## Caveats {#caveats} Because context uses reference identity to determine when to re-render, there are some gotchas that could trigger unintentional renders in consumers when a provider's parent re-renders. For example, the code below will re-render all consumers every time the Provider re-renders because a new object is always created for `value`: diff --git a/content/docs/design-principles.md b/content/docs/design-principles.md index 9e10f39d5b5..04b33908816 100644 --- a/content/docs/design-principles.md +++ b/content/docs/design-principles.md @@ -3,7 +3,7 @@ id: design-principles title: Design Principles layout: contributing permalink: docs/design-principles.html -prev: implementation-notes.html +prev: codebase-overview.html redirect_from: - "contributing/design-principles.html" --- @@ -22,17 +22,17 @@ The key feature of React is composition of components. Components written by dif For example, it should be possible to introduce some local state into a component without changing any of the components using it. Similarly, it should be possible to add some initialization and teardown code to any component when necessary. -There is nothing "bad" about using state or lifecycle methods in components. Like any powerful feature, they should be used in moderation, but we have no intention to remove them. On the contrary, we think they are integral parts of what makes React useful. We might enable [more functional patterns](https://github.com/reactjs/react-future/tree/master/07%20-%20Returning%20State) in the future, but both local state and lifecycle methods will be a part of that model. +There is nothing "bad" about using state or hooks in components. Like any powerful feature, they should be used in moderation, but we have no intention to remove them. On the contrary, we think they are integral parts of what makes React useful. We might enable [more functional patterns](https://github.com/reactjs/react-future/tree/master/07%20-%20Returning%20State) in the future, but both local state and hooks will be a part of that model. -Components are often described as "just functions" but in our view they need to be more than that to be useful. In React, components describe any composable behavior, and this includes rendering, lifecycle, and state. Some external libraries like [Relay](https://facebook.github.io/relay/) augment components with other responsibilities such as describing data dependencies. It is possible that those ideas might make it back into React too in some form. +Components are often described as "just functions" but in our view they need to be more than that to be useful. In React, components describe any composable behavior, and this includes rendering, hooks, and state. Some external libraries like [Relay](https://facebook.github.io/relay/) augment components with other responsibilities such as describing data dependencies. It is possible that those ideas might make it back into React too in some form. ### Common Abstraction {#common-abstraction} In general we [resist adding features](https://www.youtube.com/watch?v=4anAwXYqLG8) that can be implemented in userland. We don't want to bloat your apps with useless library code. However, there are exceptions to this. -For example, if React didn't provide support for local state or lifecycle methods, people would create custom abstractions for them. When there are multiple abstractions competing, React can't enforce or take advantage of the properties of either of them. It has to work with the lowest common denominator. +For example, if React didn't provide support for local state or hooks, people would create custom abstractions for them. When there are multiple abstractions competing, React can't enforce or take advantage of the properties of either of them. It has to work with the lowest common denominator. -This is why sometimes we add features to React itself. If we notice that many components implement a certain feature in incompatible or inefficient ways, we might prefer to bake it into React. We don't do it lightly. When we do it, it's because we are confident that raising the abstraction level benefits the whole ecosystem. State, lifecycle methods, cross-browser event normalization are good examples of this. +This is why sometimes we add features to React itself. If we notice that many components implement a certain feature in incompatible or inefficient ways, we might prefer to bake it into React. We don't do it lightly. When we do it, it's because we are confident that raising the abstraction level benefits the whole ecosystem. State, hooks, cross-browser event normalization are good examples of this. We always discuss such improvement proposals with the community. You can find some of those discussions by the ["big picture"](https://github.com/facebook/react/issues?q=is:open+is:issue+label:"Type:+Big+Picture") label on the React issue tracker. @@ -72,7 +72,7 @@ This is why React provides escape hatches to work with mutable models, and tries Even when your components are described as functions, when you use React you don't call them directly. Every component returns a [description of what needs to be rendered](/blog/2015/12/18/react-components-elements-and-instances.html#elements-describe-the-tree), and that description may include both user-written components like `` and platform-specific components like `
`. It is up to React to "unroll" `` at some point in the future and actually apply changes to the UI tree according to the render results of the components recursively. -This is a subtle distinction but a powerful one. Since you don't call that component function but let React call it, it means React has the power to delay calling it if necessary. In its current implementation React walks the tree recursively and calls render functions of the whole updated tree during a single tick. However in the future it might start [delaying some updates to avoid dropping frames](https://github.com/facebook/react/issues/6170). +This is a subtle distinction but a powerful one. Since you don't call that component function but let React call it, it means React has the power to delay calling it if necessary. In its current implementation React walks the tree recursively and calls components of the whole updated tree during a single tick. However in the future it might start [delaying some updates to avoid dropping frames](https://github.com/facebook/react/issues/6170). This is a common theme in React design. Some popular libraries implement the "push" approach where computations are performed when the new data is available. React, however, sticks to the "pull" approach where computations can be delayed until necessary. @@ -104,15 +104,15 @@ We are always looking out for ways to improve the developer experience. We love When something goes wrong, it is important that you have breadcrumbs to trace the mistake to its source in the codebase. In React, props and state are those breadcrumbs. -If you see something wrong on the screen, you can open React DevTools, find the component responsible for rendering, and then see if the props and state are correct. If they are, you know that the problem is in the component’s `render()` function, or some function that is called by `render()`. The problem is isolated. +If you see something wrong on the screen, you can open React DevTools, find the component responsible for rendering, and then see if the props and state are correct. If they are, you know that the problem is in the component, or some function that is called by the component. The problem is isolated. -If the state is wrong, you know that the problem is caused by one of the `setState()` calls in this file. This, too, is relatively simple to locate and fix because usually there are only a few `setState()` calls in a single file. +If the state is wrong, you know that the problem is caused by one of the state hooks in this file. This, too, is relatively simple to locate and fix because usually there are only a few `useState()` calls in a single file. If the props are wrong, you can traverse the tree up in the inspector, looking for the component that first "poisoned the well" by passing bad props down. This ability to trace any UI to the data that produced it in the form of current props and state is very important to React. It is an explicit design goal that state is not "trapped" in closures and combinators, and is available to React directly. -While the UI is dynamic, we believe that synchronous `render()` functions of props and state turn debugging from guesswork into a boring but finite procedure. We would like to preserve this constraint in React even though it makes some use cases, like complex animations, harder. +While the UI is dynamic, we believe that synchronous function components of props and state turn debugging from guesswork into a boring but finite procedure. We would like to preserve this constraint in React even though it makes some use cases, like complex animations, harder. ### Configuration {#configuration} diff --git a/content/docs/error-boundaries.md b/content/docs/error-boundaries.md deleted file mode 100644 index a259c064524..00000000000 --- a/content/docs/error-boundaries.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -id: error-boundaries -title: Error Boundaries -permalink: docs/error-boundaries.html ---- - -In the past, JavaScript errors inside components used to corrupt React’s internal state and cause it to [emit](https://github.com/facebook/react/issues/4026) [cryptic](https://github.com/facebook/react/issues/6895) [errors](https://github.com/facebook/react/issues/8579) on next renders. These errors were always caused by an earlier error in the application code, but React did not provide a way to handle them gracefully in components, and could not recover from them. - - -## Introducing Error Boundaries {#introducing-error-boundaries} - -A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an “error boundary”. - -Error boundaries are React components that **catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI** instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them. - -> Note -> -> Error boundaries do **not** catch errors for: -> -> * Event handlers ([learn more](#how-about-event-handlers)) -> * Asynchronous code (e.g. `setTimeout` or `requestAnimationFrame` callbacks) -> * Server side rendering -> * Errors thrown in the error boundary itself (rather than its children) - -A class component becomes an error boundary if it defines either (or both) of the lifecycle methods [`static getDerivedStateFromError()`](/docs/react-component.html#static-getderivedstatefromerror) or [`componentDidCatch()`](/docs/react-component.html#componentdidcatch). Use `static getDerivedStateFromError()` to render a fallback UI after an error has been thrown. Use `componentDidCatch()` to log error information. - -```js{7-10,12-15,18-21} -class ErrorBoundary extends React.Component { - constructor(props) { - super(props); - this.state = { hasError: false }; - } - - static getDerivedStateFromError(error) { - // Update state so the next render will show the fallback UI. - return { hasError: true }; - } - - componentDidCatch(error, errorInfo) { - // You can also log the error to an error reporting service - logErrorToMyService(error, errorInfo); - } - - render() { - if (this.state.hasError) { - // You can render any custom fallback UI - return

Something went wrong.

; - } - - return this.props.children; - } -} -``` - -Then you can use it as a regular component: - -```js - - - -``` - -Error boundaries work like a JavaScript `catch {}` block, but for components. Only class components can be error boundaries. In practice, most of the time you’ll want to declare an error boundary component once and use it throughout your application. - -Note that **error boundaries only catch errors in the components below them in the tree**. An error boundary can’t catch an error within itself. If an error boundary fails trying to render the error message, the error will propagate to the closest error boundary above it. This, too, is similar to how catch {} block works in JavaScript. - -## Live Demo {#live-demo} - -Check out [this example of declaring and using an error boundary](https://codepen.io/gaearon/pen/wqvxGa?editors=0010) with [React 16](/blog/2017/09/26/react-v16.0.html). - - -## Where to Place Error Boundaries {#where-to-place-error-boundaries} - -The granularity of error boundaries is up to you. You may wrap top-level route components to display a “Something went wrong” message to the user, just like server-side frameworks often handle crashes. You may also wrap individual widgets in an error boundary to protect them from crashing the rest of the application. - - -## New Behavior for Uncaught Errors {#new-behavior-for-uncaught-errors} - -This change has an important implication. **As of React 16, errors that were not caught by any error boundary will result in unmounting of the whole React component tree.** - -We debated this decision, but in our experience it is worse to leave corrupted UI in place than to completely remove it. For example, in a product like Messenger leaving the broken UI visible could lead to somebody sending a message to the wrong person. Similarly, it is worse for a payments app to display a wrong amount than to render nothing. - -This change means that as you migrate to React 16, you will likely uncover existing crashes in your application that have been unnoticed before. Adding error boundaries lets you provide better user experience when something goes wrong. - -For example, Facebook Messenger wraps content of the sidebar, the info panel, the conversation log, and the message input into separate error boundaries. If some component in one of these UI areas crashes, the rest of them remain interactive. - -We also encourage you to use JS error reporting services (or build your own) so that you can learn about unhandled exceptions as they happen in production, and fix them. - - -## Component Stack Traces {#component-stack-traces} - -React 16 prints all errors that occurred during rendering to the console in development, even if the application accidentally swallows them. In addition to the error message and the JavaScript stack, it also provides component stack traces. Now you can see where exactly in the component tree the failure has happened: - -Error caught by Error Boundary component - -You can also see the filenames and line numbers in the component stack trace. This works by default in [Create React App](https://github.com/facebookincubator/create-react-app) projects: - -Error caught by Error Boundary component with line numbers - -If you don’t use Create React App, you can add [this plugin](https://www.npmjs.com/package/babel-plugin-transform-react-jsx-source) manually to your Babel configuration. Note that it’s intended only for development and **must be disabled in production**. - -> Note -> -> Component names displayed in the stack traces depend on the [`Function.name`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name) property. If you support older browsers and devices which may not yet provide this natively (e.g. IE 11), consider including a `Function.name` polyfill in your bundled application, such as [`function.name-polyfill`](https://github.com/JamesMGreene/Function.name). Alternatively, you may explicitly set the [`displayName`](/docs/react-component.html#displayname) property on all your components. - - -## How About try/catch? {#how-about-trycatch} - -`try` / `catch` is great but it only works for imperative code: - -```js -try { - showButton(); -} catch (error) { - // ... -} -``` - -However, React components are declarative and specify *what* should be rendered: - -```js -; - } -} -``` - -#### Class Properties (Stage 3 Proposal) {#class-properties-stage-3-proposal} - -```jsx -class Foo extends Component { - // Note: this syntax is experimental and not standardized yet. - handleClick = () => { - console.log('Click happened'); - } - render() { - return ; - } -} -``` - -#### Bind in Render {#bind-in-render} - -```jsx -class Foo extends Component { - handleClick() { - console.log('Click happened'); - } - render() { - return ; - } -} -``` - ->**Note:** -> ->Using `Function.prototype.bind` in render creates a new function each time the component renders, which may have performance implications (see below). - -#### Arrow Function in Render {#arrow-function-in-render} - -```jsx -class Foo extends Component { - handleClick() { - console.log('Click happened'); - } - render() { - return ; - } -} -``` - ->**Note:** -> ->Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison. - -### Is it OK to use arrow functions in render methods? {#is-it-ok-to-use-arrow-functions-in-render-methods} +### Is it OK to use functions in render methods? {#is-it-ok-to-use-functions-in-render-methods} Generally speaking, yes, it is OK, and it is often the easiest way to pass parameters to callback functions. If you do have performance issues, by all means, optimize! -### Why is binding necessary at all? {#why-is-binding-necessary-at-all} - -In JavaScript, these two code snippets are **not** equivalent: - -```js -obj.method(); -``` - -```js -var method = obj.method; -method(); -``` - -Binding methods helps ensure that the second snippet works the same way as the first one. - -With React, typically you only need to bind the methods you *pass* to other components. For example, ` -} +// Wrong: handleClick is called instead of passed as a reference! +return ``` Instead, *pass the function itself* (without parens): ```jsx -render() { - // Correct: handleClick is passed as a reference! - return -} +// Correct: handleClick is passed as a reference! +return ``` ### How do I pass a parameter to an event handler or callback? {#how-do-i-pass-a-parameter-to-an-event-handler-or-callback} -You can use an arrow function to wrap around an event handler and pass parameters: +You can use a function to wrap around an event handler and pass parameters: ```jsx -; - } + return ; - handleClick() { - this.props.loadMore(); + function handleClick() { + props.loadMore(); } } ``` @@ -267,37 +143,31 @@ Debouncing ensures that a function will not be executed until after a certain am ```jsx import debounce from 'lodash.debounce'; -class Searchbox extends React.Component { - constructor(props) { - super(props); - this.handleChange = this.handleChange.bind(this); - this.emitChangeDebounced = debounce(this.emitChange, 250); - } +function Searchbox(props) { + const emitChangeDebounced = useCallback(debounce(emitChange, 250), []); - componentWillUnmount() { - this.emitChangeDebounced.cancel(); - } + useEffect(() => { + return () => emitChangeDebounced.cancel(); + }); - render() { - return ( - - ); - } + return ( + + ); - handleChange(e) { + function handleChange(e) { // React pools events, so we read the value before debounce. // Alternately we could call `event.persist()` and pass the entire event. // For more info see reactjs.org/docs/events.html#event-pooling - this.emitChangeDebounced(e.target.value); + emitChangeDebounced(e.target.value); } - emitChange(value) { - this.props.onChange(value); + function emitChange(value) { + props.onChange(value); } } ``` @@ -313,39 +183,31 @@ class Searchbox extends React.Component { ```jsx import rafSchedule from 'raf-schd'; -class ScrollListener extends React.Component { - constructor(props) { - super(props); +function ScrollListener(props) { + // Create a new function to schedule updates. + const scheduleUpdate = useCallback(rafSchedule( + point => props.onScroll(point) + ), []); - this.handleScroll = this.handleScroll.bind(this); - - // Create a new function to schedule updates. - this.scheduleUpdate = rafSchedule( - point => this.props.onScroll(point) - ); - } - - handleScroll(e) { + function handleScroll(e) { // When we receive a scroll event, schedule an update. // If we receive many updates within a frame, we'll only publish the latest value. - this.scheduleUpdate({ x: e.clientX, y: e.clientY }); + scheduleUpdate({ x: e.clientX, y: e.clientY }); } - componentWillUnmount() { + function componentWillUnmount() { // Cancel any pending updates since we're unmounting. - this.scheduleUpdate.cancel(); + scheduleUpdate.cancel(); } - render() { - return ( -
- -
- ); - } + return ( +
+ +
+ ); } ``` diff --git a/content/docs/faq-state.md b/content/docs/faq-state.md index c2b7b6657c1..be63b362486 100644 --- a/content/docs/faq-state.md +++ b/content/docs/faq-state.md @@ -6,13 +6,13 @@ layout: docs category: FAQ --- -### What does `setState` do? {#what-does-setstate-do} +### What does `setState` (second return value of `useState`) do? {#what-does-setstate-do} -`setState()` schedules an update to a component's `state` object. When state changes, the component responds by re-rendering. +`setState()` schedules an update to the hook's state value. When state changes, the component responds by re-rendering. ### What is the difference between `state` and `props`? {#what-is-the-difference-between-state-and-props} -[`props`](/docs/components-and-props.html) (short for "properties") and [`state`](/docs/state-and-lifecycle.html) are both plain JavaScript objects. While both hold information that influences the output of render, they are different in one important way: `props` get passed *to* the component (similar to function parameters) whereas `state` is managed *within* the component (similar to variables declared within a function). +While both hold information that influences the output of render, they are different in one important way: `props` get passed *to* the component (similar to function parameters) whereas `state` is managed *within* the component (similar to variables declared within a function). Here are some good resources for further reading on when to use `props` vs `state`: * [Props vs State](https://github.com/uberVU/react-guide/blob/master/props-vs-state.md) @@ -20,28 +20,28 @@ Here are some good resources for further reading on when to use `props` vs `stat ### Why is `setState` giving me the wrong value? {#why-is-setstate-giving-me-the-wrong-value} -In React, both `this.props` and `this.state` represent the *rendered* values, i.e. what's currently on the screen. +In React, both `props` and `state` represent the *rendered* values, i.e. what's currently on the screen. -Calls to `setState` are asynchronous - don't rely on `this.state` to reflect the new value immediately after calling `setState`. Pass an updater function instead of an object if you need to compute values based on the current state (see below for details). +Calls to `setState` are asynchronous - don't rely on `state` to reflect the new value immediately after calling `setState`. Pass an updater function instead of an object if you need to compute values based on the current state (see below for details). Example of code that will *not* behave as expected: ```jsx -incrementCount() { +function incrementCount() { // Note: this will *not* work as intended. - this.setState({count: this.state.count + 1}); + setCount(count + 1); } -handleSomething() { - // Let's say `this.state.count` starts at 0. - this.incrementCount(); - this.incrementCount(); - this.incrementCount(); - // When React re-renders the component, `this.state.count` will be 1, but you expected 3. +function handleSomething() { + // Let's say `count` starts at 0. + incrementCount(); + incrementCount(); + incrementCount(); + // When React re-renders the component, `count` will be 1, but you expected 3. - // This is because `incrementCount()` function above reads from `this.state.count`, - // but React doesn't update `this.state.count` until the component is re-rendered. - // So `incrementCount()` ends up reading `this.state.count` as 0 every time, and sets it to 1. + // This is because `incrementCount()` function above reads from `count`, + // but React doesn't update `count` until the component is re-rendered. + // So `incrementCount()` ends up reading `count` as 0 every time, and sets it to 1. // The fix is described below! } @@ -53,44 +53,36 @@ See below for how to fix this problem. Pass a function instead of an object to `setState` to ensure the call always uses the most updated version of state (see below). -### What is the difference between passing an object or a function in `setState`? {#what-is-the-difference-between-passing-an-object-or-a-function-in-setstate} +### What is the difference between passing a value or function in `setState`? {#what-is-the-difference-between-passing-a-value-or-function-in-setstate} Passing an update function allows you to access the current state value inside the updater. Since `setState` calls are batched, this lets you chain updates and ensure they build on top of each other instead of conflicting: ```jsx -incrementCount() { - this.setState((state) => { - // Important: read `state` instead of `this.state` when updating. - return {count: state.count + 1} +function incrementCount() { + setCount((state) => { + // Important: read `state.count` instead of `count` when updating. + return state.count + 1 }); } -handleSomething() { - // Let's say `this.state.count` starts at 0. - this.incrementCount(); - this.incrementCount(); - this.incrementCount(); +function handleSomething() { + // Let's say `count` starts at 0. + incrementCount(); + incrementCount(); + incrementCount(); - // If you read `this.state.count` now, it would still be 0. + // If you read `count` now, it would still be 0. // But when React re-renders the component, it will be 3. } ``` [Learn more about setState](/docs/react-component.html#setstate) -### When is `setState` asynchronous? {#when-is-setstate-asynchronous} - -Currently, `setState` is asynchronous inside event handlers. - -This ensures, for example, that if both `Parent` and `Child` call `setState` during a click event, `Child` isn't re-rendered twice. Instead, React "flushes" the state updates at the end of the browser event. This results in significant performance improvements in larger apps. - -This is an implementation detail so avoid relying on it directly. In the future versions, React will batch updates by default in more cases. - -### Why doesn't React update `this.state` synchronously? {#why-doesnt-react-update-thisstate-synchronously} +### Why doesn't React update `state` synchronously? {#why-doesnt-react-update-state-synchronously} As explained in the previous section, React intentionally "waits" until all components call `setState()` in their event handlers before starting to re-render. This boosts performance by avoiding unnecessary re-renders. -However, you might still be wondering why React doesn't just update `this.state` immediately without re-rendering. +However, you might still be wondering why React doesn't just update `state` immediately without re-rendering. There are two main reasons: diff --git a/content/docs/faq-styling.md b/content/docs/faq-styling.md index 7436c3b0786..d85fbf85fa2 100644 --- a/content/docs/faq-styling.md +++ b/content/docs/faq-styling.md @@ -11,21 +11,17 @@ category: FAQ Pass a string as the `className` prop: ```jsx -render() { - return Menu -} +return Menu ``` It is common for CSS classes to depend on the component props or state: ```jsx -render() { - let className = 'menu'; - if (this.props.isActive) { - className += ' menu-active'; - } - return Menu +let className = 'menu'; +if (props.isActive) { + className += ' menu-active'; } +return Menu ``` >Tip diff --git a/content/docs/faq-versioning.md b/content/docs/faq-versioning.md index ab45ddf7449..9dc83e4ef4a 100644 --- a/content/docs/faq-versioning.md +++ b/content/docs/faq-versioning.md @@ -26,7 +26,7 @@ Instead, we release new features in minor versions. That means that minor releas ### Commitment to Stability {#commitment-to-stability} -As we change React over time, we try to minimize the effort required to take advantage of new features. When possible, we'll keep an older API working, even if that means putting it in a separate package. For example, [mixins have been discouraged for years](/blog/2016/07/13/mixins-considered-harmful.html) but they're supported to this day [via create-react-class](/docs/react-without-es6.html#mixins) and many codebases continue to use them in stable, legacy code. +As we change React over time, we try to minimize the effort required to take advantage of new features. When possible, we'll keep an older API working, even if that means putting it in a separate package. For example, [mixins have been discouraged for years](/blog/2016/07/13/mixins-considered-harmful.html) but they're supported to this day via create-react-class and many codebases continue to use them in stable, legacy code. Over a million developers use React, collectively maintaining millions of components. The Facebook codebase alone has over 50,000 React components. That means we need to make it as easy as possible to upgrade to new versions of React; if we make large changes without a migration path, people will be stuck on old versions. We test these upgrade paths on Facebook itself – if our team of less than 10 people can update 50,000+ components alone, we hope the upgrade will be manageable for anyone using React. In many cases, we write [automated scripts](https://github.com/reactjs/react-codemod) to upgrade component syntax, which we then include in the open-source release for everyone to use. diff --git a/content/docs/forms.md b/content/docs/forms.md index 1a8b599d556..a309b01a00d 100644 --- a/content/docs/forms.md +++ b/content/docs/forms.md @@ -31,48 +31,40 @@ We can combine the two by making the React state be the "single source of truth" For example, if we want to make the previous example log the name when it is submitted, we can write the form as a controlled component: -```javascript{4,10-12,24} -class NameForm extends React.Component { - constructor(props) { - super(props); - this.state = {value: ''}; - - this.handleChange = this.handleChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - } +```javascript{2,4-6,17} +function NameForm() { + const [value, setValue] = useState(''); - handleChange(event) { - this.setState({value: event.target.value}); + function handleChange(event) { + setValue(event.target.value); } - handleSubmit(event) { - alert('A name was submitted: ' + this.state.value); + function handleSubmit(event) { + alert('A name was submitted: ' + value); event.preventDefault(); } - render() { - return ( -
- - -
- ); - } + return ( +
+ + +
+ ); } ``` [**Try it on CodePen**](https://codepen.io/gaearon/pen/VmmPgp?editors=0010) -Since the `value` attribute is set on our form element, the displayed value will always be `this.state.value`, making the React state the source of truth. Since `handleChange` runs on every keystroke to update the React state, the displayed value will update as the user types. +Since the `value` attribute is set on our form element, the displayed value will always be `value` from `useState`, making the React state the source of truth. Since `handleChange` runs on every keystroke to update the React state, the displayed value will update as the user types. With a controlled component, every state mutation will have an associated handler function. This makes it straightforward to modify or validate user input. For example, if we wanted to enforce that names are written with all uppercase letters, we could write `handleChange` as: ```javascript{2} handleChange(event) { - this.setState({value: event.target.value.toUpperCase()}); + setValue(event.target.value.toUpperCase()); } ``` @@ -88,42 +80,32 @@ In HTML, a `