From 439584a6f4070f34a92dd62ae1217d4009d8bcdd Mon Sep 17 00:00:00 2001 From: Nat Alison Date: Mon, 11 Mar 2019 12:48:20 -0700 Subject: [PATCH 01/11] Update languages.yml (#1808) * Brazilian Portuguese is complete! * Update status of Hebrew, Polish, German --- content/languages.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/languages.yml b/content/languages.yml index ff0be2c1d..d34224dda 100644 --- a/content/languages.yml +++ b/content/languages.yml @@ -26,7 +26,7 @@ - name: German translated_name: Deutsch code: de - status: 0 + status: 1 - name: Greek translated_name: Ελληνικά code: el @@ -50,7 +50,7 @@ - name: Hebrew translated_name: עברית code: he - status: 0 + status: 1 - name: Hindi translated_name: हिन्दी code: hi @@ -102,11 +102,11 @@ - name: Polish translated_name: Polski code: pl - status: 0 + status: 1 - name: Portuguese (Brazil) translated_name: Português do Brasil code: pt-br - status: 1 + status: 2 - name: Portuguese (Portugal) translated_name: Português europeu code: pt-pt From d827d5958a7d01b278b5e6e3428d6d6676ae7dd0 Mon Sep 17 00:00:00 2001 From: Nat Alison Date: Wed, 13 Mar 2019 03:34:06 -0700 Subject: [PATCH 02/11] Update languages.yml (#1820) * Russian is complete! * Move some other languages up --- content/languages.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/languages.yml b/content/languages.yml index d34224dda..5e0958260 100644 --- a/content/languages.yml +++ b/content/languages.yml @@ -78,7 +78,7 @@ - name: Korean translated_name: 한국어 code: ko - status: 0 + status: 1 - name: Kurdish translated_name: کوردی‎ code: ku @@ -118,7 +118,7 @@ - name: Russian translated_name: Русский code: ru - status: 1 + status: 2 - name: Sinhala translated_name: සිංහල code: si @@ -137,7 +137,7 @@ - name: Turkish translated_name: Türkçe code: tr - status: 0 + status: 1 - name: Ukrainian translated_name: Українська code: uk From 2cd4d0cf5ddadf90446b3a5038a9bc4875151355 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 13:59:42 +0000 Subject: [PATCH 03/11] Add more info to FAQ about deps (#1815) * Add more info to FAQ about deps * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-effect.md Co-Authored-By: gaearon * tweaks * tweak --- content/docs/hooks-custom.md | 24 ++-- content/docs/hooks-effect.md | 37 +++-- content/docs/hooks-faq.md | 248 +++++++++++++++++++++++++++++++- content/docs/hooks-reference.md | 41 ++++-- content/docs/hooks-rules.md | 3 +- 5 files changed, 316 insertions(+), 37 deletions(-) diff --git a/content/docs/hooks-custom.md b/content/docs/hooks-custom.md index ac9dad6da..a252ac3f3 100644 --- a/content/docs/hooks-custom.md +++ b/content/docs/hooks-custom.md @@ -18,11 +18,11 @@ import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -44,11 +44,11 @@ import React, { useState, useEffect } from 'react'; function FriendListItem(props) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -79,11 +79,11 @@ import React, { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 64b32476e..fd7d89592 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -198,17 +198,17 @@ 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} +```js{6-16} import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Specify how to clean up after this effect: return function cleanup() { @@ -237,6 +237,10 @@ We've learned that `useEffect` lets us express different kinds of side effects a ```js useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -316,15 +320,15 @@ function FriendStatusWithCounter(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); - - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } // ... } ``` @@ -394,6 +398,7 @@ Now consider the version of this component that uses Hooks: function FriendStatus(props) { // ... useEffect(() => { + // ... ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -449,8 +454,12 @@ When we render with `count` updated to `6`, React will compare the items in the This also works for effects that have a cleanup phase: -```js{6} +```js{10} useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -462,9 +471,13 @@ In the future, the second argument might get added automatically by a build-time >Note > ->If you use this optimization, make sure the array includes **any values from the outer scope that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. We'll also discuss other optimization options in the [Hooks API reference](/docs/hooks-reference.html). +>If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and [what to do when the array changes too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often). +> +>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works. +> +>If you pass an empty array (`[]`), the props and state inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. > ->If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works. While passing `[]` is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, we suggest not making it a habit because it often leads to bugs, [as discussed above](#explanation-why-effects-run-on-each-update). Don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. +>We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. ## Next Steps {#next-steps} diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index b3b99de90..6145b968d 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -32,16 +32,20 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo * [What exactly do the lint rules enforce?](#what-exactly-do-the-lint-rules-enforce) * **[From Classes to Hooks](#from-classes-to-hooks)** * [How do lifecycle methods correspond to Hooks?](#how-do-lifecycle-methods-correspond-to-hooks) + * [How can I do data fetching with Hooks?](#how-can-i-do-data-fetching-with-hooks) * [Is there something like instance variables?](#is-there-something-like-instance-variables) * [Should I use one or many state variables?](#should-i-use-one-or-many-state-variables) * [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) + * [Why am I seeing stale props or state inside my function?](#why-am-i-seeing-stale-props-or-state-inside-my-function) * [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)** * [Can I skip an effect on updates?](#can-i-skip-an-effect-on-updates) + * [Is it safe to omit functions from the list of dependencies?](#is-it-safe-to-omit-functions-from-the-list-of-dependencies) + * [What can I do if my effect dependencies change too often?](#what-can-i-do-if-my-effect-dependencies-change-too-often) * [How do I implement shouldComponentUpdate?](#how-do-i-implement-shouldcomponentupdate) * [How to memoize calculations?](#how-to-memoize-calculations) * [How to create expensive objects lazily?](#how-to-create-expensive-objects-lazily) @@ -204,6 +208,10 @@ There are a few more heuristics, and they might change over time as we fine-tune * `componentDidCatch` and `getDerivedStateFromError`: There are no Hook equivalents for these methods yet, but they will be added soon. +### How can I do data fetching with Hooks? + +Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. + ### Is there something like instance variables? {#is-there-something-like-instance-variables} Yes! The [`useRef()`](/docs/hooks-reference.html#useref) Hook isn't just for DOM refs. The "ref" object is a generic container whose `current` property is mutable and can hold any value, similar to an instance property on a class. @@ -362,6 +370,44 @@ It's possible that in the future React will provide a `usePrevious` Hook out of See also [the recommended pattern for derived state](#how-do-i-implement-getderivedstatefromprops). +### Why am I seeing stale props or state inside my function? {#why-am-i-seeing-stale-props-or-state-inside-my-function} + +Any function inside a component, including event handlers and effects, "sees" the props and state from the render it was created in. For example, consider code like this: + +```js +function Example() { + const [count, setCount] = useState(0); + + function handleAlertClick() { + setTimeout(() => { + alert('You clicked on: ' + count); + }, 3000); + } + + return ( +
+

You clicked {count} times

+ + +
+ ); +} +``` + +If you first click "Show alert" and then increment the counter, the alert will show the `count` variable **at the time you clicked the "Show alert" button**. This prevents bugs caused by the code assuming props and state don't change. + +If you intentionally want to read the *latest* state from some asynchronous callback, you could keep it in [a ref](/docs/hooks-faq.html#is-there-something-like-instance-variables), mutate it, and read from it. + +Finally, another possible reason you're seeing stale props or state is if you use the "dependency array" optimization but didn't correctly specify all the dependencies. For example, if an effect specifies `[]` as the second argument but reads `someProp` inside, it will keep "seeing" the initial value of `someProp`. The solution is to either remove the dependency array, or to fix it. Here's [how you can deal with functions](#is-it-safe-to-omit-functions-from-the-list-of-dependencies), and here's [other common strategies](#what-can-i-do-if-my-effect-dependencies-change-too-often) to run effects less often without incorrectly skipping dependencies. + +>Note +> +>We provide an [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) ESLint rule as a part of the [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. + ### How do I implement `getDerivedStateFromProps`? {#how-do-i-implement-getderivedstatefromprops} While you probably [don't need it](/blog/2018/06/07/you-probably-dont-need-derived-state.html), in rare cases that you do (such as implementing a `` component), you can update the state right during rendering. React will re-run the component with updated state immediately after exiting the first render so it wouldn't be expensive. @@ -416,6 +462,207 @@ If you're not familiar with this syntax, check out the [explanation](/docs/hooks Yes. See [conditionally firing an effect](/docs/hooks-reference.html#conditionally-firing-an-effect). Note that forgetting to handle updates often [introduces bugs](/docs/hooks-effect.html#explanation-why-effects-run-on-each-update), which is why this isn't the default behavior. +### Is it safe to omit functions from the list of dependencies? {#is-it-safe-to-omit-functions-from-the-list-of-dependencies} + +Generally speaking, no. + +```js{3,8} +function Example() { + function doSomething() { + console.log(someProp); + } + + useEffect(() => { + doSomething(); + }, []); // 🔴 This is not safe (it calls `doSomething` which uses `someProp`) +} +``` + +It's difficult to remember which props or state are used by functions outside of the effect. This is why **usually you'll want to declare functions needed by an effect *inside* of it.** Then it's easy to see what values from the component scope that effect depends on: + +```js{4,8} +function Example() { + useEffect(() => { + function doSomething() { + console.log(someProp); + } + + doSomething(); + }, [someProp]); // ✅ OK (our effect only uses `someProp`) +} +``` + +If after that we still don't use any values from the component scope, it's safe to specify `[]`: + +```js{7} +useEffect(() => { + function doSomething() { + console.log('hello'); + } + + doSomething(); +}, []); // ✅ OK in this example because we don't use *any* values from component scope +``` + +Depending on your use case, there are a few more options described below. + +>Note +> +>We provide the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) ESLint rule as a part of the [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It help you find components that don't handle updates consistently. + +Let's see why this matters. + +If you specify a [list of dependencies](/docs/hooks-reference.html#conditionally-firing-an-effect) as the last argument to `useEffect`, `useMemo`, `useCallback`, or `useImperativeHandle`, it must include all values used inside that participate in the React data flow. That includes props, state, and anything derived from them. + +It is **only** safe to omit a function from the dependency list if nothing in it (or the functions called by it) references props, state, or values derived from them. This example has a bug: + +```js{5,12} +function ProductPage({ productId }) { + const [product, setProduct] = useState(null); + + async function fetchProduct() { + const response = await fetch('http://myapi/product' + productId); // Uses productId prop + const json = await response.json(); + setProduct(json); + } + + useEffect(() => { + fetchProduct(); + }, []); // 🔴 Invalid because `fetchProduct` uses `productId` + // ... +} +``` + +**The recommended fix is to move that function _inside_ of your effect**. That makes it easy to see which props or state your effect uses, and to ensure they're all declared: + +```js{5-10,13} +function ProductPage({ productId }) { + const [product, setProduct] = useState(null); + + useEffect(() => { + // By moving this function inside the effect, we can clearly see the values it uses. + async function fetchProduct() { + const response = await fetch('http://myapi/product' + productId); + const json = await response.json(); + setProduct(json); + } + + fetchProduct(); + }, [productId]); // ✅ Valid because our effect only uses productId + // ... +} +``` + +This also allows you to handle out-of-order responses with a local variable inside the effect: + +```js{2,6,8} + useEffect(() => { + let ignore = false; + async function fetchProduct() { + const response = await fetch('http://myapi/product/' + productId); + const json = await response.json(); + if (!ignore) setProduct(json); + } + return () => { ignore = true }; + }, [productId]); +``` + +We moved the function inside the effect so it doesn't need to be in its dependency list. + +>Tip +> +>Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. + +**If for some reason you _can't_ move a function inside an effect, there are a few more options:** + +* **You can try moving that function outside of your component**. In that case, the function is guaranteed to not reference any props or state, and also doesn't need to be in the list of dependencies. +* If the function you're calling is a pure computation and is safe to call while rendering, you may **call it outside of the effect instead,** and make the effect depend on the returned value. +* As a last resort, you can **add a function to effect dependencies but _wrap its definition_** into the [`useCallback`](/docs/hooks-reference.html#usecallback) Hook. This ensures it doesn't change on every render unless *its own* dependencies also change: + +```js{2-5} +function ProductPage({ productId }) { + // ✅ Wrap with useCallback to avoid change on every render + const fetchProduct = useCallback(() => { + // ... Does something with productId ... + }, [productId]); // ✅ All useCallback dependencies are specified + + return ; +} + +function ProductDetails({ fetchProduct }) + useEffect(() => { + fetchProduct(); + }, [fetchProduct]); // ✅ All useEffect dependencies are specified + // ... +} +``` + +Note that in the above example we **need** to keep the function in the dependencies list. This ensures that a change in the `productId` prop of `ProductPage` automatically triggers a refetch in the `ProductDetails` component. + +### What can I do if my effect dependencies change too often? + +Sometimes, your effect may be using reading state that changes too often. You might be tempted to omit that state from a list of dependencies, but that usually leads to bugs: + +```js{6,9} +function Counter() { + const [count, setCount] = useState(0); + + useEffect(() => { + const id = setInterval(() => { + setCount(count + 1); // This effect depends on the `count` state + }, 1000); + return () => clearInterval(id); + }, []); // 🔴 Bug: `count` is not specified as a dependency + + return

{count}

; +} +``` + +Specifying `[count]` as a list of dependencies would fix the bug, but would cause the interval to be reset on every change. That may not be desirable. To fix this, we can use the [functional update form of `setState`](/docs/hooks-reference.html#functional-updates). It lets us specify *how* the state needs to change without referencing the *current* state: + +```js{6,9} +function Counter() { + const [count, setCount] = useState(0); + + useEffect(() => { + const id = setInterval(() => { + setCount(c => c + 1); // ✅ This doesn't depend on `count` variable outside + }, 1000); + return () => clearInterval(id); + }, []); // ✅ Our effect doesn't use any variables in the component scope + + return

{count}

; +} +``` + +(The identity of the `setCount` function is guaranteed to be stable so it's safe to omit.) + +In more complex cases (such as if one state depends on another state), try moving the state update logic outside the effect with the [`useReducer` Hook](/docs/hooks-reference.html#usereducer). [This article](https://adamrackis.dev/state-and-use-reducer/) offers an example of how you can do this. **The identity of the `dispatch` function from `useReducer` is always stable** — even if the reducer function is declared inside the component and reads its props. + +As a last resort, if you want to something like `this` in a class, you can [use a ref](/docs/hooks-faq.html#is-there-something-like-instance-variables) to hold a mutable variable. Then you can write and read to it. For example: + +```js{2-6,10-11,16} +function Example(props) { + // Keep latest props in a ref. + let latestProps = useRef(props); + useEffect(() => { + latestProps.current = props; + }); + + useEffect(() => { + function tick() { + // Read latest props at any time + console.log(latestProps.current); + } + + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, []); // This effect never re-runs +} +``` + +Only do this if you couldn't find a better alternative, as relying on mutation makes components less predictable. If there's a specific pattern that doesn't translate well, [file an issue](https://github.com/facebook/react/issues/new) with a runnable example code and we can try to help. + ### How do I implement `shouldComponentUpdate`? {#how-do-i-implement-shouldcomponentupdate} You can wrap a function component with `React.memo` to shallowly compare its props: @@ -430,7 +677,6 @@ It's not a Hook because it doesn't compose like Hooks do. `React.memo` is equiva `React.memo` doesn't compare state because there is no single state object to compare. But you can make children pure too, or even [optimize individual children with `useMemo`](/docs/hooks-faq.html#how-to-memoize-calculations). - ### How to memoize calculations? {#how-to-memoize-calculations} The [`useMemo`](/docs/hooks-reference.html#usememo) Hook lets you cache calculations between multiple renders by "remembering" the previous computation: diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index deeef54cf..4466bc83b 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -45,6 +45,10 @@ setState(newState); During subsequent re-renders, the first value returned by `useState` will always be the most recent state after applying updates. +>Note +> +>React guarantees that `setState` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list. + #### Functional updates {#functional-updates} If the new state is computed using the previous state, you can pass a function to `setState`. The function will receive the previous value, and return an updated value. Here's an example of a counter component that uses both forms of `setState`: @@ -133,7 +137,7 @@ Although `useEffect` is deferred until after the browser has painted, it's guara #### Conditionally firing an effect {#conditionally-firing-an-effect} -The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its inputs changes. +The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes. However, this may be overkill in some cases, like the subscription example from the previous section. We don't need to create a new subscription on every update, only if the `source` props has changed. @@ -153,11 +157,18 @@ useEffect( Now the subscription will only be recreated when `props.source` changes. -Passing in an empty array `[]` of inputs tells React that your effect doesn't depend on any values from the component, so that effect would run only on mount and clean up on unmount; it won't run on updates. - -> Note +>Note +> +>If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and what to do when the [array values change too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often). +> +>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the dependencies array always works. > -> The array of inputs is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically. +>If you pass an empty array (`[]`), the props and state as inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. +> +> +>We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. + +The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. ### `useContext` {#usecontext} @@ -211,6 +222,10 @@ function Counter({initialState}) { } ``` +>Note +> +>React guarantees that `dispatch` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list. + #### Specifying the initial state {#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: @@ -283,13 +298,15 @@ const memoizedCallback = useCallback( Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) callback. -Pass an inline callback and an array of inputs. `useCallback` will return a memoized version of the callback that only changes if one of the inputs has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`). +Pass an inline callback and an array of dependencies. `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`). -`useCallback(fn, inputs)` is equivalent to `useMemo(() => fn, inputs)`. +`useCallback(fn, deps)` is equivalent to `useMemo(() => fn, deps)`. > Note > -> The array of inputs is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically. +> The array of dependencies is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +> +> We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. ### `useMemo` {#usememo} @@ -299,7 +316,7 @@ const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) value. -Pass a "create" function and an array of inputs. `useMemo` will only recompute the memoized value when one of the inputs has changed. This optimization helps to avoid expensive calculations on every render. +Pass a "create" function and an array of dependencies. `useMemo` will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render. Remember that the function passed to `useMemo` runs during rendering. Don't do anything there that you wouldn't normally do while rendering. For example, side effects belong in `useEffect`, not `useMemo`. @@ -309,7 +326,9 @@ If no array is provided, a new value will be computed whenever a new function in > Note > -> The array of inputs is not passed as arguments to the function. Conceptually, though, that's what they represent: every value referenced inside the function should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically. +> The array of dependencies is not passed as arguments to the function. Conceptually, though, that's what they represent: every value referenced inside the function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +> +> We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. ### `useRef` {#useref} @@ -342,7 +361,7 @@ Note that `useRef()` is useful for more than the `ref` attribute. It's [handy fo ### `useImperativeHandle` {#useimperativehandle} ```js -useImperativeHandle(ref, createHandle, [inputs]) +useImperativeHandle(ref, createHandle, [deps]) ``` `useImperativeHandle` customizes the instance value that is exposed to parent components when using `ref`. As always, imperative code using refs should be avoided in most cases. `useImperativeHandle` should be used with `forwardRef`: diff --git a/content/docs/hooks-rules.md b/content/docs/hooks-rules.md index 698d1c741..8c332578c 100644 --- a/content/docs/hooks-rules.md +++ b/content/docs/hooks-rules.md @@ -40,7 +40,8 @@ npm install eslint-plugin-react-hooks ], "rules": { // ... - "react-hooks/rules-of-hooks": "error" + "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks + "react-hooks/exhaustive-deps": "warning" // Checks effect dependencies } } ``` From 83faa76713205d2c669005d6d51d29ee65079cf7 Mon Sep 17 00:00:00 2001 From: Victoria Quirante Date: Wed, 13 Mar 2019 16:28:05 +0100 Subject: [PATCH 04/11] Add React Alicante 2019 (#1819) --- content/community/conferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/community/conferences.md b/content/community/conferences.md index 89ed871d0..e138b119a 100644 --- a/content/community/conferences.md +++ b/content/community/conferences.md @@ -76,6 +76,11 @@ September 26-28, 2019 in Goa, India [Website](https://www.reactindia.io/) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) +### React Alicante 2019 {#react-alicante-2019} +September 26-28, 2019 in Alicante, Spain + +[Website](http://reactalicante.es/) - [Twitter](https://twitter.com/reactalicante) - [Facebook](https://www.facebook.com/ReactAlicante) + ## Past Conferences {#past-conferences} ### React.js Conf 2015 {#reactjs-conf-2015} From 110a318d1efc7d2c60644bd160dcac359b8778fb Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 15:38:51 +0000 Subject: [PATCH 05/11] Link to a data fetching demo --- content/docs/hooks-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 6145b968d..1f36f5f17 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -210,7 +210,7 @@ There are a few more heuristics, and they might change over time as we fine-tune ### How can I do data fetching with Hooks? -Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +Here is a [small demo](https://codesandbox.io/s/pwm32zx7z7) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. ### Is there something like instance variables? {#is-there-something-like-instance-variables} From 474de38b2c1e9dc8d9e6cc30b1cb0e8ff4ae3628 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 15:42:01 +0000 Subject: [PATCH 06/11] Use another example --- content/docs/hooks-faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 1f36f5f17..df6d9f7c3 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -210,7 +210,7 @@ There are a few more heuristics, and they might change over time as we fine-tune ### How can I do data fetching with Hooks? -Here is a [small demo](https://codesandbox.io/s/pwm32zx7z7) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +Here is a [small demo](https://codesandbox.io/s/jvvkoo8pq3) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. ### Is there something like instance variables? {#is-there-something-like-instance-variables} @@ -571,7 +571,7 @@ We moved the function inside the effect so it doesn't need to be in its dependen >Tip > ->Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +>Check out [this small demo](https://codesandbox.io/s/jvvkoo8pq3) and [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. **If for some reason you _can't_ move a function inside an effect, there are a few more options:** From 877b1897588c7c4640ecf1f6841adfe3986c44b8 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 15:46:05 +0000 Subject: [PATCH 07/11] Fix wording --- content/docs/hooks-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index df6d9f7c3..0540031ba 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -210,7 +210,7 @@ There are a few more heuristics, and they might change over time as we fine-tune ### How can I do data fetching with Hooks? -Here is a [small demo](https://codesandbox.io/s/jvvkoo8pq3) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +Here is a [small demo](https://codesandbox.io/s/jvvkoo8pq3) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) about data fetching with Hooks. ### Is there something like instance variables? {#is-there-something-like-instance-variables} From 352c3ff01f437de2441533313bb1feb2c3f87414 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 18:24:10 +0000 Subject: [PATCH 08/11] Fix example --- content/docs/hooks-faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 0540031ba..9016fa7f4 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -467,7 +467,7 @@ Yes. See [conditionally firing an effect](/docs/hooks-reference.html#conditional Generally speaking, no. ```js{3,8} -function Example() { +function Example({ someProp }) { function doSomething() { console.log(someProp); } @@ -481,7 +481,7 @@ function Example() { It's difficult to remember which props or state are used by functions outside of the effect. This is why **usually you'll want to declare functions needed by an effect *inside* of it.** Then it's easy to see what values from the component scope that effect depends on: ```js{4,8} -function Example() { +function Example({ someProp }) { useEffect(() => { function doSomething() { console.log(someProp); From 3e489fc2f9a028d570ccfdd74ddbc93faea27d32 Mon Sep 17 00:00:00 2001 From: Manuel Bieh Date: Thu, 14 Mar 2019 01:09:09 +0100 Subject: [PATCH 09/11] Fixing incorrect value for eslint rule (#1824) The name for the "warning" level in ESLint is `warn` not `warning` (https://eslint.org/docs/user-guide/configuring#configuring-rules) --- content/docs/hooks-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-rules.md b/content/docs/hooks-rules.md index 8c332578c..dbac9cffd 100644 --- a/content/docs/hooks-rules.md +++ b/content/docs/hooks-rules.md @@ -41,7 +41,7 @@ npm install eslint-plugin-react-hooks "rules": { // ... "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks - "react-hooks/exhaustive-deps": "warning" // Checks effect dependencies + "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies } } ``` From 40bbbd5b102cd33bdee85fcc4bd209dc06585975 Mon Sep 17 00:00:00 2001 From: Dimitar Nestorov Date: Thu, 14 Mar 2019 16:49:11 +0200 Subject: [PATCH 10/11] Updating useMemo docs (#1796) https://codesandbox.io/s/4x68k8nrw --- content/docs/hooks-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index 4466bc83b..4daf0f191 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -320,7 +320,7 @@ Pass a "create" function and an array of dependencies. `useMemo` will only recom Remember that the function passed to `useMemo` runs during rendering. Don't do anything there that you wouldn't normally do while rendering. For example, side effects belong in `useEffect`, not `useMemo`. -If no array is provided, a new value will be computed whenever a new function instance is passed as the first argument. (With an inline function, on every render.) +If no array is provided, a new value will be computed on every render. **You may rely on `useMemo` as a performance optimization, not as a semantic guarantee.** In the future, React may choose to "forget" some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without `useMemo` — and then add it to optimize performance. From 1fe2e0ae29b2fe8a8a09ab10048bb9fe284ff568 Mon Sep 17 00:00:00 2001 From: Nelson Reitz Date: Fri, 15 Mar 2019 15:04:19 +0100 Subject: [PATCH 11/11] Fix overflowing headers on small screens (#1830) --- src/theme.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/theme.js b/src/theme.js index 9f440d56d..a4d445206 100644 --- a/src/theme.js +++ b/src/theme.js @@ -89,6 +89,11 @@ const fonts = { lineHeight: '65px', fontWeight: 700, + [media.lessThan('small')]: { + overflowWrap: 'break-word', + wordBreak: 'break-word', + }, + [media.lessThan('medium')]: { fontSize: 40, lineHeight: '45px',