From d818c19f6f55f7255cca8137832c7a71031858c7 Mon Sep 17 00:00:00 2001 From: imed jaberi <43971542+3imed-jaberi@users.noreply.github.com> Date: Thu, 8 Aug 2019 00:33:59 +0100 Subject: [PATCH 01/36] =?UTF-8?q?=20fix=20'Going=20Big=20with=20React'=20p?= =?UTF-8?q?art=20=F0=9F=91=BB=20..=20(#2192)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/community/videos.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/community/videos.md b/content/community/videos.md index d50312231..01e54f753 100644 --- a/content/community/videos.md +++ b/content/community/videos.md @@ -53,8 +53,7 @@ Facebook engineers Bill Fisher and Jing Chen talk about Flux and React at Forwar ### Going Big with React {#going-big-with-react} Areeb Malik investigates how React performs in a high stress situation, and how it helped his team build safe code on a massive scale - (2014 - 0h31m). -[![going big with React](https://i.vimeocdn.com/video/481670116_650.jpg)] - + ### Rethinking Best Practices {#rethinking-best-practices} From ccdcf455e7cf9058df637a83698ee95a55e1925d Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Thu, 8 Aug 2019 20:53:40 +0100 Subject: [PATCH 02/36] Update Footer link to --- src/components/LayoutFooter/Footer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/LayoutFooter/Footer.js b/src/components/LayoutFooter/Footer.js index 5e571d971..43b7713fa 100644 --- a/src/components/LayoutFooter/Footer.js +++ b/src/components/LayoutFooter/Footer.js @@ -87,10 +87,10 @@ const Footer = ({layoutHasSidebar = false}: {layoutHasSidebar: boolean}) => ( Stack Overflow - Discussion Forum + Discussion Forums Date: Thu, 8 Aug 2019 14:17:59 -0700 Subject: [PATCH 03/36] Profiler API docs (#2176) * First pass at Profiler API docs --- content/docs/nav.yml | 2 + content/docs/reference-profiler.md | 119 +++++++++++++++++++++++++++++ package.json | 3 +- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 content/docs/reference-profiler.md diff --git a/content/docs/nav.yml b/content/docs/nav.yml index 4d4a7571f..8e98ea4d0 100644 --- a/content/docs/nav.yml +++ b/content/docs/nav.yml @@ -59,6 +59,8 @@ title: Optimizing Performance - id: portals title: Portals + - id: profiler + title: Profiler - id: react-without-es6 title: React Without ES6 - id: react-without-jsx diff --git a/content/docs/reference-profiler.md b/content/docs/reference-profiler.md new file mode 100644 index 000000000..a7cc5f279 --- /dev/null +++ b/content/docs/reference-profiler.md @@ -0,0 +1,119 @@ +--- +id: profiler +title: Profiler API +layout: docs +category: Reference +permalink: docs/profiler.html +--- + +The `Profiler` measures how often a React application renders and what the "cost" of rendering is. +Its purpose is to help identify parts of an application that are slow and may benefit from [optimizations such as memoization](https://reactjs.org/docs/hooks-faq.html#how-to-memoize-calculations). + +> Note: +> +> Profiling adds some additional overhead, so **it is disabled in [the production build](https://reactjs.org/docs/optimizing-performance.html#use-the-production-build)**. +> +> To opt into production profiling, React provides a special production build with profiling enabled. +> Read more about how to use this build at [fb.me/react-profiling](https://fb.me/react-profiling) + +## Usage + +A `Profiler` can be added anywhere in a React tree to measure the cost of rendering that part of the tree. +It requires two props: an `id` (string) and an `onRender` callback (function) which React calls any time a component within the tree "commits" an update. + +For example, to profile a `Navigation` component and its descendants: + +```js{3} +render( + + + + +
+ +); +``` + +Multiple `Profiler` components can be used to measure different parts of an application: +```js{3,6} +render( + + + + + +
+ + +); +``` + +`Profiler` components can also be nested to measure different components within the same subtree: +```js{2,6,8} +render( + + + + + + + + + + + + +); +``` + +> Note +> +> Although `Profiler` is a light-weight component, it should be used only when necessary; each use adds some CPU and memory overhead to an application. + +## `onRender` Callback + +The `Profiler` requires an `onRender` function as a prop. +React calls calls this function any time a component within the profiled tree "commits" an update. +It receives parameters describing what was rendered and how long it took. + +```js +function onRenderCallback( + id, // the "id" prop of the Profiler tree that has just committed + phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered) + actualDuration, // time spent rendering the committed update + baseDuration, // estimated time to render the entire subtree without memoization + startTime, // when React began rendering this update + commitTime, // when React committed this update + interactions // the Set of interactions belonging to this update +) { + // Aggregate or log render timings... +} +``` + +Let's take a closer look at each of the props: + +* **`id: string`** - +The `id` prop of the `Profiler` tree that has just committed. +This can be used to identify which part of the tree was committed if you are using multiple profilers. +* **`phase: "mount" | "update"`** - +Identifies whether the tree has just been mounted for the first time or re-rendered due to a change in props, state, or hooks. +* **`actualDuration: number`** - +Time spent rendering the `Profiler` and its descendants for the current update. +This indicates how well the subtree makes use of memoization (e.g. [`React.memo`](/docs/react-api.html#reactmemo), [`useMemo`](/docs/hooks-reference.html#usememo), [`shouldComponentUpdate`](/docs/hooks-faq.html#how-do-i-implement-shouldcomponentupdate)). +Ideally this value should decrease significantly after the initial mount as many of the descendants will only need to re-render if their specific props change. +* **`baseDuration: number`** - +Duration of the most recent `render` time for each individual component within the `Profiler` tree. +This value estimates a worst-case cost of rendering (e.g. the initial mount or a tree with no memoization). +* **`startTime: number`** - +Timestamp when React began rendering the current update. +* **`commitTime: number`** - +Timestamp when React committed the current update. +This value is shared between all profilers in a commit, enabling them to be grouped if desirable. +* **`interactions: Set`** - +Set of ["interactions"](http://fb.me/react-interaction-tracing) that were being traced the update was scheduled (e.g. when `render` or `setState` were called). + +> Note +> +> Interactions can be used to identify the cause of an update, althoguh the API for tracing them is still experimental. +> +> Learn more about it at [fb.me/react-interaction-tracing](http://fb.me/react-interaction-tracing) \ No newline at end of file diff --git a/package.json b/package.json index ba61ccc95..10ed4e01a 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,8 @@ "nit:examples": "prettier --config examples/.prettierrc --list-different \"examples/**/*.js\"", "prettier": "yarn format:source && yarn format:examples", "prettier:diff": "yarn nit:source && yarn nit:examples", - "reset": "rimraf ./.cache" + "reset": "rimraf ./.cache", + "start": "yarn dev" }, "devDependencies": { "@babel/preset-flow": "^7.0.0", From 4867765f37f0903034c4b667385faa54e01d8652 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 8 Aug 2019 14:35:09 -0700 Subject: [PATCH 04/36] Update website to v16.9 (#2210) --- content/versions.yml | 4 ++++ package.json | 4 ++-- src/site-constants.js | 2 +- yarn.lock | 27 +++++++++++++-------------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/content/versions.yml b/content/versions.yml index 8f1ca21be..8d1f92abb 100644 --- a/content/versions.yml +++ b/content/versions.yml @@ -1,5 +1,9 @@ +- title: '16.9' + changelog: https://github.com/facebook/react/blob/master/CHANGELOG.md#1690-august-8-2019 - title: '16.8' + path: /version/16.8 changelog: https://github.com/facebook/react/blob/master/CHANGELOG.md#1680-february-6-2019 + url: https://5d4b5feba32acd0008d0df98--reactjs.netlify.com/ - title: '16.7' path: /version/16.7 changelog: https://github.com/facebook/react/blob/master/CHANGELOG.md#1670-december-19-2018 diff --git a/package.json b/package.json index 10ed4e01a..b22d19551 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,8 @@ "normalize.css": "^8.0.0", "prettier": "^1.7.4", "prismjs": "^1.15.0", - "react": "16.8.6", - "react-dom": "16.8.6", + "react": "^16.9.0", + "react-dom": "^16.9.0", "react-helmet": "^5.2.0", "react-live": "1.8.0-0", "remarkable": "^1.7.1", diff --git a/src/site-constants.js b/src/site-constants.js index 4618432e0..f109e71bb 100644 --- a/src/site-constants.js +++ b/src/site-constants.js @@ -8,7 +8,7 @@ // NOTE: We can't just use `location.toString()` because when we are rendering // the SSR part in node.js we won't have a proper location. const urlRoot = 'https://reactjs.org'; -const version = '16.8.6'; +const version = '16.9.0'; const babelURL = 'https://unpkg.com/babel-standalone@6.26.0/babel.min.js'; export {babelURL, urlRoot, version}; diff --git a/yarn.lock b/yarn.lock index fd392c929..480594945 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10241,15 +10241,15 @@ react-dev-utils@^4.2.1: strip-ansi "3.0.1" text-table "0.2.0" -react-dom@16.8.6: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" - integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA== +react-dom@^16.9.0: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962" + integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" + scheduler "^0.15.0" react-error-overlay@^3.0.0: version "3.0.0" @@ -10303,15 +10303,14 @@ react-side-effect@^1.1.0: exenv "^1.2.1" shallowequal "^1.0.1" -react@16.8.6: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" - integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== +react@^16.9.0: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa" + integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" read-all-stream@^3.0.0: version "3.1.0" @@ -11020,10 +11019,10 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.13.6: - version "0.13.6" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" - integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== +scheduler@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e" + integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" From 8be8bf9d2f22e6d06768f1fcb89cf5ad6f01e5ba Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Thu, 8 Aug 2019 22:43:59 +0100 Subject: [PATCH 05/36] Testing Docs (#2165) * Testing Docs This expands the testing docs for reactjs.org. It adds 3 main docs - - testing.md: An entry point to the testing docs - testing-recipes.md: Common patterns when writing tests for React Components. - testing-environments.md: Setting up your testing environment. With help from @alexkrolick, @kentcdodds, @gaearon --- content/blog/2019-02-06-react-v16.8.0.md | 4 +- content/community/nav.yml | 2 - content/community/tools-testing.md | 13 - content/docs/addons-test-utils.md | 6 +- content/docs/hooks-faq.md | 2 +- content/docs/nav.yml | 8 + content/docs/reference-test-renderer.md | 31 ++ content/docs/testing-environments.md | 58 +++ content/docs/testing-recipes.md | 595 +++++++++++++++++++++++ content/docs/testing.md | 40 ++ 10 files changed, 739 insertions(+), 20 deletions(-) delete mode 100644 content/community/tools-testing.md create mode 100644 content/docs/testing-environments.md create mode 100644 content/docs/testing-recipes.md create mode 100644 content/docs/testing.md diff --git a/content/blog/2019-02-06-react-v16.8.0.md b/content/blog/2019-02-06-react-v16.8.0.md index ef7641fb2..f761b0b58 100644 --- a/content/blog/2019-02-06-react-v16.8.0.md +++ b/content/blog/2019-02-06-react-v16.8.0.md @@ -50,7 +50,7 @@ Even while Hooks were in alpha, the React community created many interesting [ex ## Testing Hooks {#testing-hooks} -We have added a new API called `ReactTestUtils.act()` in this release. It ensures that the behavior in your tests matches what happens in the browser more closely. We recommend to wrap any code rendering and triggering updates to your components into `act()` calls. Testing libraries can also wrap their APIs with it (for example, [`react-testing-library`](https://github.com/kentcdodds/react-testing-library)'s `render` and `fireEvent` utilities do this). +We have added a new API called `ReactTestUtils.act()` in this release. It ensures that the behavior in your tests matches what happens in the browser more closely. We recommend to wrap any code rendering and triggering updates to your components into `act()` calls. Testing libraries can also wrap their APIs with it (for example, [`react-testing-library`](https://testing-library.com/react)'s `render` and `fireEvent` utilities do this). For example, the counter example from [this page](/docs/hooks-effect.html) can be tested like this: @@ -95,7 +95,7 @@ The calls to `act()` will also flush the effects inside of them. If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote. -To reduce the boilerplate, we recommend using [`react-testing-library`](https://git.io/react-testing-library) which is designed to encourage writing tests that use your components as the end users do. +To reduce the boilerplate, we recommend using [`react-testing-library`](https://testing-library.com/react) which is designed to encourage writing tests that use your components as the end users do. ## Thanks {#thanks} diff --git a/content/community/nav.yml b/content/community/nav.yml index 0f59b80a0..48350d85b 100644 --- a/content/community/nav.yml +++ b/content/community/nav.yml @@ -34,8 +34,6 @@ title: Model Management - id: data-fetching title: Data Fetching - - id: testing - title: Testing - id: ui-components title: UI Components - id: misc diff --git a/content/community/tools-testing.md b/content/community/tools-testing.md deleted file mode 100644 index 4c62ec78c..000000000 --- a/content/community/tools-testing.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -id: testing -title: Testing -layout: community -permalink: community/testing.html ---- - -* **[Enzyme](https://github.com/airbnb/enzyme/):** a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output. -* **[Jest](https://facebook.github.io/jest/):** Delightful JavaScript testing used by Facebook to test all JavaScript code including React applications. -* **[react-testing-library](https://github.com/kentcdodds/react-testing-library):** 🐐 Simple and complete React DOM testing utilities that encourage good testing practices. -* **[React-unit](https://github.com/pzavolinsky/react-unit):** a lightweight unit test library for ReactJS with very few (js-only) dependencies. -* **[Skin-deep](https://github.com/glenjamin/skin-deep):** Testing helpers for use with React's shallowRender test utils. -* **[Unexpected-react](https://github.com/bruderstein/unexpected-react/):** Plugin for the [unexpected](https://unexpected.js.org/) assertion library that makes it easy to assert over your React Components and trigger events. diff --git a/content/docs/addons-test-utils.md b/content/docs/addons-test-utils.md index 2eb9f3b33..b5eb6ecf0 100644 --- a/content/docs/addons-test-utils.md +++ b/content/docs/addons-test-utils.md @@ -122,7 +122,9 @@ it('can render and update a counter', () => { }); ``` -Don't forget that dispatching DOM events only works when the DOM container is added to the `document`. You can use a helper like [`react-testing-library`](https://github.com/kentcdodds/react-testing-library) to reduce the boilerplate code. +- Don't forget that dispatching DOM events only works when the DOM container is added to the `document`. You can use a library like [React Testing Library](https://testing-library.com/react) to reduce the boilerplate code. + +- The [`recipes`](/docs/recipes.html) document contains more details on how `act()` behaves, with examples and usage. * * * @@ -139,7 +141,7 @@ Pass a mocked component module to this method to augment it with useful methods > Note: > -> `mockComponent()` is a legacy API. We recommend using [shallow rendering](/docs/shallow-renderer.html) or [`jest.mock()`](https://facebook.github.io/jest/docs/en/tutorial-react-native.html#mock-native-modules-using-jestmock) instead. +> `mockComponent()` is a legacy API. We recommend using [`jest.mock()`](https://facebook.github.io/jest/docs/en/tutorial-react-native.html#mock-native-modules-using-jestmock) instead. * * * diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 3e0cf6290..1a2c4336a 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -180,7 +180,7 @@ The calls to `act()` will also flush the effects inside of them. If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote. -To reduce the boilerplate, we recommend using [`react-testing-library`](https://git.io/react-testing-library) which is designed to encourage writing tests that use your components as the end users do. +To reduce the boilerplate, we recommend using [React Testing Library](https://testing-library.com/react) which is designed to encourage writing tests that use your components as the end users do. ### What exactly do the [lint rules](https://www.npmjs.com/package/eslint-plugin-react-hooks) enforce? {#what-exactly-do-the-lint-rules-enforce} diff --git a/content/docs/nav.yml b/content/docs/nav.yml index 8e98ea4d0..4a481fc01 100644 --- a/content/docs/nav.yml +++ b/content/docs/nav.yml @@ -125,6 +125,14 @@ title: Hooks API Reference - id: hooks-faq title: Hooks FAQ +- title: Testing + items: + - id: testing + title: Testing Overview + - id: testing-recipes + title: Testing Recipes + - id: testing-environments + title: Testing Environments - title: Contributing items: - id: how-to-contribute diff --git a/content/docs/reference-test-renderer.md b/content/docs/reference-test-renderer.md index d53a257d4..f2b50ccd4 100644 --- a/content/docs/reference-test-renderer.md +++ b/content/docs/reference-test-renderer.md @@ -70,6 +70,7 @@ expect(testInstance.findByProps({className: "sub"}).children).toEqual(['Sub']); ### TestRenderer {#testrenderer} * [`TestRenderer.create()`](#testrenderercreate) +* [`TestRenderer.act()`](#testrendereract) ### TestRenderer instance {#testrenderer-instance} @@ -104,6 +105,36 @@ TestRenderer.create(element, options); Create a `TestRenderer` instance with the passed React element. It doesn't use the real DOM, but it still fully renders the component tree into memory so you can make assertions about it. The returned instance has the following methods and properties. +### `TestRenderer.act()` {#testrendereract} + +```javascript +TestRenderer.act(callback); +``` + +Similar to the [`act()` helper from `react-dom/test-utils`](/docs/test-utils.html#act), `TestRenderer.act` prepares a component for assertions. Use this version of `act()` to wrap calls to `TestRenderer.create` and `testRenderer.update`. + +```javascript +import {create, act} from 'react-test-renderer'; +import App from './app.js'; // The component being tested + +// render the component +let root; +act(() => { + root = create() +}); + +// make assertions on root +expect(root.toJSON()).toMatchSnapshot(); + +// update with some different props +act(() => { + root = root.update(); +}) + +// make assertions on root +expect(root.toJSON()).toMatchSnapshot(); +``` + ### `testRenderer.toJSON()` {#testrenderertojson} ```javascript diff --git a/content/docs/testing-environments.md b/content/docs/testing-environments.md new file mode 100644 index 000000000..5120a4f6d --- /dev/null +++ b/content/docs/testing-environments.md @@ -0,0 +1,58 @@ +--- +id: testing-environments +title: Testing Environments +permalink: docs/testing-environments.html +prev: testing-recipes.html +--- + + + +This document goes through the factors that can affect your environment and recommendations for some scenarios. + +### Test runners {#test-runners} + +Test runners like [Jest](https://jestjs.io/), [mocha](https://mochajs.org/), [ava](https://github.com/avajs/ava) let you write test suites as regular JavaScript, and run them as part of your development process. Additionally, test suites are run as part of continuous integration. + +- Jest is widely compatible with React projects, supporting features like mocked [modules](#mocking-modules) and [timers](#mocking-timers), and [`jsdom`](#mocking-a-rendering-surface) support. **If you use Create React App, [Jest is already included out of the box](https://facebook.github.io/create-react-app/docs/running-tests) with useful defaults.** +- Libraries like [mocha](https://mochajs.org/#running-mocha-in-the-browser) work well in real browser environments, and could help for tests that explicitly need it. +- End-to-end tests are used for testing longer flows across multiple pages, and require a [different setup](#end-to-end-tests-aka-e2e-tests). + +### Mocking a rendering surface {#mocking-a-rendering-surface} + +Tests often run in an environment without access to a real rendering surface like a browser. For these environments, we recommend simulating a browser with [`jsdom`](https://github.com/jsdom/jsdom), a lightweight browser implementation that runs inside Node.js. + +In most cases, jsdom behaves like a regular browser would, but doesn't have features like [layout and navigation](https://github.com/jsdom/jsdom#unimplemented-parts-of-the-web-platform). This is still useful for most web-based component tests, since it runs quicker than having to start up a browser for each test. It also runs in the same process as your tests, so you can write code to examine and assert on the rendered DOM. + +Just like in a real browser, jsdom lets us model user interactions; tests can dispatch events on DOM nodes, and then observe and assert on the side effects of these actions [(example)](/docs/testing-recipes.html#events). + +A large portion of UI tests can be written with the above setup: using Jest as a test runner, rendered to jsdom, with user interactions specified as sequences of browser events, powered by the `act()` helper [(example)](/docs/testing-recipes.html). For example, a lot of React's own tests are written with this combination. + +If you're writing a library that tests mostly browser-specific behavior, and requires native browser behavior like layout or real inputs, you could use a framework like [mocha.](https://mochajs.org/) + +In an environment where you _can't_ simulate a DOM (e.g. testing React Native components on Node.js), you could use [event simulation helpers](https://reactjs.org/docs/test-utils.html#simulate) to simulate interactions with elements. Alternately, you could use the `fireEvent` helper from [`@testing-library/react-native`](https://testing-library.com/docs/native-testing-library). + +Frameworks like [Cypress](https://www.cypress.io/), [puppeteer](https://github.com/GoogleChrome/puppeteer) and [webdriver](https://www.seleniumhq.org/projects/webdriver/) are useful for running [end-to-end tests](#end-to-end-tests-aka-e2e-tests). + +### Mocking functions {#mocking-functions} + +When writing tests, we'd like to mock out the parts of our code that don't have equivalents inside our testing environment (e.g. checking `navigator.onLine` status inside Node.js). Tests could also spy on some functions, and observe how other parts of the test interact with them. It is then useful to be able to selectively mock these functions with test-friendly versions. + +This is especially useful for data fetching. It is usually preferable to use "fake" data for tests to avoid the slowness and flakiness due to fetching from real API endpoints [(example)](/docs/testing-recipes.html#data-fetching). This helps make the tests predictable. Libraries like [Jest](https://jestjs.io/) and [sinon](https://sinonjs.org/), among others, support mocked functions. For end-to-end tests, mocking network can be more difficult, but you might also want to test the real API endpoints in them anyway. + +### Mocking modules {#mocking-modules} + +Some components have dependencies for modules that may not work well in test environments, or aren't essential to our tests. It can be useful to selectively mock these modules out with suitable replacements [(example)](/docs/testing-recipes.html#mocking-modules). + +On Node.js, runners like Jest [support mocking modules](https://jestjs.io/docs/en/manual-mocks). You could also use libraries like [`mock-require`](https://www.npmjs.com/package/mock-require). + +### Mocking timers {#mocking-timers} + +Components might be using time-based functions like `setTimeout`, `setInterval`, or `Date.now`. In testing environments, it can be helpful to mock these functions out with replacements that let you manually "advance" time. This is great for making sure your tests run fast! Tests that are dependent on timers would still resolve in order, but quicker [(example)](/docs/testing-recipes.html#timers). Most frameworks, including [Jest](https://jestjs.io/docs/en/timer-mocks), [sinon](https://sinonjs.org/releases/v7.3.2/fake-timers/) and [lolex](https://github.com/sinonjs/lolex), let you mock timers in your tests. + +Sometimes, you may not want to mock timers. For example, maybe you're testing an animation, or interacting with an endpoint that's sensitive to timing (like an API rate limiter). Libraries with timer mocks let you enable and disable them on a per test/suite basis, so you can explicitly choose how these tests would run. + +### End-to-end tests {#end-to-end-tests-aka-e2e-tests} + +End-to-end tests are useful for testing longer workflows, especially when they're critical to your business (such as payments or signups). For these tests, you'd probably want to test both how a real browser renders the whole app, fetches data from the real API endpoints, uses sessions and cookies, navigates between different links. You might also likely want to make assertions not just on the DOM state, but on the backing data as well (e.g. to verify whether the updates have been persisted to the database). + +In this scenario, you would use a framework like [Cypress](https://www.cypress.io/) or a library like [puppeteer](https://github.com/GoogleChrome/puppeteer) so you can navigate between multiple routes and assert on side effects not just in the browser, but potentially on the backend as well. diff --git a/content/docs/testing-recipes.md b/content/docs/testing-recipes.md new file mode 100644 index 000000000..bf536cc20 --- /dev/null +++ b/content/docs/testing-recipes.md @@ -0,0 +1,595 @@ +--- +id: testing-recipes +title: Testing Recipes +permalink: docs/testing-recipes.html +prev: testing.html +next: testing-environments.html +--- + +Common testing patterns for React components. + +> Note: +> +> This page assumes you're using [Jest](https://jestjs.io/) as a test runner. If you use a different test runner, you may need to adjust the API, but the overall shape of the solution will likely be the same. Read more details on setting up a testing environment on the [Testing Environments](/docs/testing-environments.html) page. + +On this page, we will primarily use function components. However, these testing strategies don't depend on implementation details, and work just as well for class components too. + +### Setup / Teardown {#setup--teardown} + +For each test, we usually want to render our React tree to a DOM element that's attached to `document`. This is important so that it can receive DOM events. When the test ends, we want to "clean up" and unmount the tree from the `document`. + +A common way to do it is to use a pair of `beforeEach` and `afterEach` blocks so that they'll always run and isolate the effects of a test to itself: + +```jsx +import { unmountComponentAtNode } from "react-dom"; + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); +``` + +You may use a different pattern, but keep in mind that we want to execute the cleanup _even if a test fails_. Otherwise, tests can become "leaky", and one test can change the behavior of another test. That makes them difficult to debug. + +### `act()` {#act} + +When writing UI tests, tasks like rendering, user events, or data fetching can be considered as "units" of interaction with a user interface. React provides a helper called `act()` that makes sure all updates related to these "units" have been processed and applied to the DOM before you make any assertions: + +```js +act(() => { + // render components +}); +// make assertions +``` + +This helps make your tests run closer to what real users would experience when using your application. The rest of these examples use `act()` to make these guarantees. + +You might find using `act()` directly a bit too verbose. To avoid some of the boilerplate, you could use a library like [React Testing Library](https://testing-library.com/react), whose helpers are wrapped with `act()`. + +> Note: +> +> The name `act` comes from the [Arrange-Act-Assert](http://wiki.c2.com/?ArrangeActAssert) pattern. + +### Rendering {#rendering} + +Commonly, you might want to test whether a component renders correctly for given props. Consider a simple component that renders a message based on a prop: + +```jsx +// hello.js + +import React from "react"; + +export default function Hello(props) { + if (props.name) { + return

Hello, {props.name}!

; + } else { + return Hey, stranger; + } +} +``` + +We can write a test for this component: + +```jsx{24-27} +// hello.test.js + +import React from "react"; +import { render, unmountComponentAtNode } from "react-dom"; +import { act } from "react-dom/test-utils"; + +import Hello from "./hello"; + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); + +it("renders with or without a name", () => { + act(() => { + render(, container); + }); + expect(container.textContent).toBe("Hey, stranger"); + + act(() => { + render(, container); + }); + expect(container.textContent).toBe("Hello, Jenny!"); + + act(() => { + render(, container); + }); + expect(container.textContent).toBe("Hello, Margaret!"); +}); +``` + +### Data fetching {#data-fetching} + +Instead of calling real APIs in all your tests, you can mock requests with dummy data. Mocking data fetching with "fake" data prevents flaky tests due to an unavailable backend, and makes them run faster. Note: you may still want to run a subset of tests using an ["end-to-end"](/docs/testing-environments.html#end-to-end-tests-aka-e2e-tests) framework that tells whether the whole app is working together. + +```jsx +// user.js + +import React, { useState, useEffect } from "react"; + +export default function User(props) { + const [user, setUser] = useState(null); + + async function fetchUserData(id) { + const response = await fetch("/" + id); + setUser(await response.json()); + } + + useEffect(() => { + fetchUserData(props.id); + }, [props.id]); + + if (!user) { + return "loading..."; + } + + return ( +
+ {user.name} + {user.age} years old +
+ lives in {user.address} +
+ ); +} +``` + +We can write tests for it: + +```jsx{23-33,44-45} +// user.test.js + +import React from "react"; +import { render, unmountComponentAtNode } from "react-dom"; +import { act } from "react-dom/test-utils"; +import User from "./user"; + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); + +it("renders user data", async () => { + const fakeUser = { + name: "Joni Baez", + age: "32", + address: "123, Charming Avenue" + }; + + jest.spyOn(global, "fetch").mockImplementation(() => + Promise.resolve({ + json: () => Promise.resolve(fakeUser) + }) + ); + + // Use the asynchronous version of act to apply resolved promises + await act(async () => { + render(, container); + }); + + expect(container.querySelector("summary").textContent).toBe(fakeUser.name); + expect(container.querySelector("strong").textContent).toBe(fakeUser.age); + expect(container.textContent).toContain(fakeUser.address); + + // remove the mock to ensure tests are completely isolated + global.fetch.mockRestore(); +}); +``` + +### Mocking modules {#mocking-modules} + +Some modules might not work well inside a testing environment, or may not be as essential to the test itself. Mocking out these modules with dummy replacements can make it easier to write tests for your own code. + +Consider a `Contact` component that embeds a third-party `GoogleMap` component: + +```jsx +// map.js + +import React from "react"; + +import { LoadScript, GoogleMap } from "react-google-maps"; +export default function Map(props) { + return ( + + + + ); +} + +// contact.js + +import React from "react"; +import Map from "./map"; + +function Contact(props) { + return ( +
+
+ Contact {props.name} via{" "} + + email + + or on their + website + . +
+ +
+ ); +} +``` + +If we don't want to load this component in our tests, we can mock out the dependency itself to a dummy component, and run our tests: + +```jsx{10-18} +// contact.test.js + +import React from "react"; +import { render, unmountComponentAtNode } from "react-dom"; +import { act } from "react-dom/test-utils"; + +import Contact from "./contact"; +import MockedMap from "./map"; + +jest.mock("./map", () => { + return function DummyMap(props) { + return ( +
+ {props.center.lat}:{props.center.long} +
+ ); + }; +}); + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); + +it("should render contact information", () => { + const center = { lat: 0, long: 0 }; + act(() => { + render( + , + container + ); + }); + + expect( + container.querySelector("[data-test-id='email']").getAttribute("href") + ).toEqual("mailto:test@example.com"); + + expect( + container.querySelector('[data-test-id="site"]').getAttribute("href") + ).toEqual("http://test.com"); + + expect(container.querySelector('[data-test-id="map"]').textContent).toEqual( + "0:0" + ); +}); +``` + +### Events {#events} + +We recommend dispatching real DOM events on DOM elements, and then asserting on the result. Consider a `Toggle` component: + +```jsx +// toggle.js + +import React, { useState } from "react"; + +export default function Toggle(props) { + const [state, setState] = useState(false); + return ( + + ); +} +``` + +We could write tests for it: + +```jsx{13-14,35,43} +// toggle.test.js + +import React from "react"; +import { render, unmountComponentAtNode } from "react-dom"; +import { act } from "react-dom/test-utils"; + +import Toggle from "./toggle"; + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + // container *must* be attached to document so events work correctly. + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); + +it("changes value when clicked", () => { + const onChange = jest.fn(); + act(() => { + render(, container); + }); + + // get a hold of the button element, and trigger some clicks on it + const button = document.querySelector("[data-testid=toggle]"); + expect(button.innerHTML).toBe("Turn off"); + + act(() => { + button.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + + expect(onChange).toHaveBeenCalledTimes(1); + expect(button.innerHTML).toBe("Turn on"); + + act(() => { + for (let i = 0; i < 5; i++) { + button.dispatchEvent(new MouseEvent("click", { bubbles: true })); + } + }); + + expect(onChange).toHaveBeenCalledTimes(6); + expect(button.innerHTML).toBe("Turn on!"); +}); +``` + +Diffrent DOM events and their properties are described in [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent). Note that you need to pass `{ bubbles: true }` in each event you create for it to reach the React listener because React automatically delegates events to the document. + +> Note: +> +> React Testing Library offers a [more concise helper](https://testing-library.com/docs/dom-testing-library/api-events) for firing events. + +### Timers {#timers} + +Your code might use timer-based functions like `setTimeout` to schedule more work in the future. In this example, a multiple choice panel waits for a selection and advances, timing out if a selection isn't made in 5 seconds: + +```jsx +//card.js + +import React, { useEffect } from "react"; + +export default function Card(props) { + useEffect(() => { + const timeoutID = setTimeout(() => { + props.onSelect(null); + }, 500); + return () => { + clearTimeout(timeoutID); + }; + }, [props.onSelect]); + + return [1, 2, 3, 4].map(choice => ( + + )); +} +``` + +We can write tests for this component by leveraging [Jest's timer mocks](https://jestjs.io/docs/en/timer-mocks), and testing the different states it can be in. + +```jsx{7,31,37,49,59} +// card.test.js + +import React from "react"; +import { render, unmountComponentAtNode } from "react-dom"; +import { act } from "react-dom/test-utils"; + +jest.useFakeTimers(); + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); + +it("should select null after timing out", () => { + const onSelect = jest.fn(); + act(() => { + render(, container); + }); + + // move ahead in time by 100ms + act(() => { + jest.advanceTimersByTime(100); + }); + expect(onSelect).not.toHaveBeenCalled(); + + // and then move ahead by 1 second + act(() => { + jest.advanceTimersByTime(1000); + }); + expect(onSelect).toHaveBeenCalledWith(null); +}); + +it("should cleanup on being removed", () => { + const onSelect = jest.fn(); + act(() => { + render(, container); + }); + + act(() => { + jest.advanceTimersByTime(100); + }); + expect(onSelect).not.toHaveBeenCalled(); + + // unmount the app + act(() => { + render(null, container); + }); + + act(() => { + jest.advanceTimersByTime(1000); + }); + expect(onSelect).not.toHaveBeenCalled(); +}); + +it("should accept selections", () => { + const onSelect = jest.fn(); + act(() => { + render(, container); + }); + + act(() => { + container + .querySelector("[data-test-id=2]") + .dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + + expect(onSelect).toHaveBeenCalledWith(2); +}); +``` + +You can use fake timers only in some tests. Above, we enabled them by calling `jest.useFakeTimers()`. The main advantage they provide is that your test doesn't actually have to wait five seconds to execute, and you also didn't need to make the component code more convoluted just for testing. + +### Snapshot testing {#snapshot-testing} + +Frameworks like Jest also let you save "snapshots" of data with [`toMatchSnapshot` / `toMatchInlineSnapshot`](https://jestjs.io/docs/en/snapshot-testing). With these, we can "save" the renderered component output and ensure that a change to it has to be explicitly committed as a change to the snapshot. + +In this example, we render a component and format the rendered HTML with the [`pretty`](https://www.npmjs.com/package/pretty) package, before saving it as an inline snapshot: + +```jsx{29-31} +// hello.test.js, again + +import React from "react"; +import { render, unmountComponentAtNode } from "react-dom"; +import { act } from "react-dom/test-utils"; +import pretty from "pretty"; + +import Hello from "./hello"; + +let container = null; +beforeEach(() => { + // setup a DOM element as a render target + container = document.createElement("div"); + document.body.appendChild(container); +}); + +afterEach(() => { + // cleanup on exiting + unmountComponentAtNode(container); + container.remove(); + container = null; +}); + +it("should render a greeting", () => { + act(() => { + render(, container); + }); + + expect( + pretty(container.innerHTML) + ).toMatchInlineSnapshot(); /* ... gets filled automatically by jest ... */ + + act(() => { + render(, container); + }); + + expect( + pretty(container.innerHTML) + ).toMatchInlineSnapshot(); /* ... gets filled automatically by jest ... */ + + act(() => { + render(, container); + }); + + expect( + pretty(container.innerHTML) + ).toMatchInlineSnapshot(); /* ... gets filled automatically by jest ... */ +}); +``` + +It's typically better to make more specific assertions than to use snapshots. These kinds of tests include implementation details so they break easily, and teams can get desensitized to snapshot breakages. Selectively [mocking some child components](#mocking-modules) can help reduce the size of snapshots and keep them readable for the code review. + +### Multiple renderers {#multiple-renderers} + +In rare cases, you may be running a test on a component that uses multiple renderers. For example, you may be running snapshot tests on a component with `react-test-renderer`, that internally uses `ReactDOM.render` inside a child component to render some content. In this scenario, you can wrap updates with `act()`s corresponding to their renderers. + +```jsx +import { act as domAct } from "react-dom/test-utils"; +import { act as testAct, create } from "react-test-renderer"; +// ... +let root; +domAct(() => { + testAct(() => { + root = create(); + }); +}); +expect(root).toMatchSnapshot(); +``` + +### Something missing? {#something-missing} + +If some common scenario is not covered, please let us know on the [issue tracker](https://github.com/reactjs/reactjs.org/issues) for the documentation website. diff --git a/content/docs/testing.md b/content/docs/testing.md new file mode 100644 index 000000000..1b9cb73a9 --- /dev/null +++ b/content/docs/testing.md @@ -0,0 +1,40 @@ +--- +id: testing +title: Testing Overview +permalink: docs/testing.html +redirect_from: + - "community/testing.html" +next: testing-recipes.html +--- + +You can test React components similar to testing other JavaScript code. + +There are a few ways to test React components. Broadly, they divide into two categories: + +* **Rendering component trees** in a simplified test environment and asserting on their output. +* **Running a complete app** in a realistic browser environment (also known as “end-to-end” tests). + +This documentation section focuses on testing strategies for the first case. While full end-to-end tests can be very useful to prevent regressions to important workflows, such tests are not concerned with React components in particular, and are out of scope of this section. + +### Tradeoffs {#tradeoffs} + + +When choosing testing tools, it is worth considering a few tradeoffs: + +* **Iteration speed vs Realistic environment:** Some tools offer a very quick feedback loop between making a change and seeing the result, but don't model the browser behavior precisely. Other tools might use a real browser environment, but reduce the iteration speed and are flakier on a continuous integration server. +* **How much to mock:** With components, the distinction between a "unit" and "integration" test can be blurry. If you're testing a form, should its test also test the buttons inside of it? Or should a button component have its own test suite? Should refactoring a button ever break the form test? + +Different answers may work for different teams and products. + +### Recommended Tools {#tools} + +**[Jest](https://facebook.github.io/jest/)** is a JavaScript test runner that lets you access the DOM via [`jsdom`](#mocking-a-rendering-surface). While jsdom is only an approximation of how the browser works, it is often good enough for testing React components. Jest provides a great iteration speed combined with powerful features like mocking [modules](#mocking-modules) and [timers](#mocking-timers) so you can have more control over how the code executes. + +**[React Testing Library](https://testing-library.com/react)** is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn't provide a way to "shallowly" render a component without its children, a test runner like Jest lets you do this by [mocking](/docs/testing-recipes.html#mocking-modules). + +### Learn more {#learn-more} + +This section is divided in two pages: + +- [Recipes](/docs/testing-recipes.html): Common patterns when writing tests for React components. +- [Environments](/docs/testing-environments.html): What to consider when setting up a testing environment for React components. From c2d1794c4644f78aac64d5b505af83044abb0f16 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 8 Aug 2019 23:08:20 +0100 Subject: [PATCH 06/36] Capitalization --- content/docs/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/testing.md b/content/docs/testing.md index 1b9cb73a9..cf2de4c44 100644 --- a/content/docs/testing.md +++ b/content/docs/testing.md @@ -32,7 +32,7 @@ Different answers may work for different teams and products. **[React Testing Library](https://testing-library.com/react)** is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn't provide a way to "shallowly" render a component without its children, a test runner like Jest lets you do this by [mocking](/docs/testing-recipes.html#mocking-modules). -### Learn more {#learn-more} +### Learn More {#learn-more} This section is divided in two pages: From a2a905117a81698efdba0424f4e20e45f7cde0dd Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 8 Aug 2019 23:42:24 +0100 Subject: [PATCH 07/36] Blog post for 16.9 (#2208) --- content/blog/2018-11-27-react-16-roadmap.md | 5 + content/blog/2019-08-08-react-v16.9.0.md | 241 ++++++++++++++++++ content/images/blog/react-v16.9.0/codemod.gif | Bin 0 -> 693048 bytes .../images/blog/react-v16.9.0/cwm-warning.png | Bin 0 -> 108981 bytes 4 files changed, 246 insertions(+) create mode 100644 content/blog/2019-08-08-react-v16.9.0.md create mode 100644 content/images/blog/react-v16.9.0/codemod.gif create mode 100644 content/images/blog/react-v16.9.0/cwm-warning.png diff --git a/content/blog/2018-11-27-react-16-roadmap.md b/content/blog/2018-11-27-react-16-roadmap.md index d8a2f3982..56fb8e87e 100644 --- a/content/blog/2018-11-27-react-16-roadmap.md +++ b/content/blog/2018-11-27-react-16-roadmap.md @@ -4,6 +4,11 @@ author: [gaearon] --- You might have heard about features like "Hooks", "Suspense", and "Concurrent Rendering" in the previous blog posts and talks. In this post, we'll look at how they fit together and the expected timeline for their availability in a stable release of React. + +> An Update from August, 2019 +> +> You can find an update to this roadmap in the [React 16.9 release blog post](/blog/2019/08/08/react-v16.9.0.html#an-update-to-the-roadmap). + ## tl;dr {#tldr} diff --git a/content/blog/2019-08-08-react-v16.9.0.md b/content/blog/2019-08-08-react-v16.9.0.md new file mode 100644 index 000000000..25c5f2041 --- /dev/null +++ b/content/blog/2019-08-08-react-v16.9.0.md @@ -0,0 +1,241 @@ +--- +title: "React v16.9.0 and the Roadmap Update" +author: [gaearon, bvaughn] +--- + +Today we are releasing React 16.9. It contains several new features, bugfixes, and new deprecation warnings to help prepare for a future major release. + +## New Deprecations {#new-deprecations} + +### Renaming Unsafe Lifecycle Methods {#renaming-unsafe-lifecycle-methods} + +[Over a year ago](/blog/2018/03/27/update-on-async-rendering.html), we've announced that unsafe lifecycle methods are getting renamed: + +* `componentWillMount` → `UNSAFE_componentWillMount` +* `componentWillReceiveProps` → `UNSAFE_componentWillReceiveProps` +* `componentWillUpdate` → `UNSAFE_componentWillUpdate` + +**React 16.9 does not contain breaking changes, and the old names continue to work in this release.** But you will now see a warning when using any of the old names: + +![Warning: componentWillMount has been renamed, and is not recommended for use.](https://i.imgur.com/sngxSML.png) + +As the warning suggests, there are usually [better approaches](/blog/2018/03/27/update-on-async-rendering.html#migrating-from-legacy-lifecycles) for each of the unsafe methods. However, maybe you don't have the time to migrate or test these components. In that case, we recommend running a ["codemod"](https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb) script that renames them automatically: + +```bash +cd your_project +npx react-codemod rename-unsafe-lifecycles +``` + +*(Note that it says `npx`, not `npm`. `npx` is a utility that comes with Node 6+ by default.)* + +Running this codemod will replace the old names like `componentWillMount` with the new names like `UNSAFE_componentWillMount`: + +![Codemod in action](https://i.imgur.com/Heyvcyi.gif) + +The new names like `UNSAFE_componentWillMount` **will keep working in both React 16.9 and in React 17.x**. However, the new `UNSAFE_` prefix will help components with problematic patterns stand out during the code review and debugging sessions. (If you'd like, you can further discourage their use inside your app with the opt-in [Strict Mode](/docs/strict-mode.html).) + +>Note +> +>Learn more about our [versioning policy and commitent to stability](/docs/faq-versioning.html#commitment-to-stability). + +### Deprecating `javascript:` URLs {#deprecating-javascript-urls} + +URLs starting with `javascript:` are a dangerous attack surface because it's easy to accidentally include unsanitized output in a tag like `` and create a security hole: + +```js +const userProfile = { + website: "javascript: alert('you got hacked')", +}; +// This will now warn: +Profile +```` + +**In React 16.9,** this pattern continues to work, but it will log a warning. If you use `javascript:` URLs for logic, try to use React event handlers instead. (As a last resort, you can circumvent the protection with [`dangerouslySetInnerHTML`](/docs/dom-elements.html#dangerouslysetinnerhtml), but it is highly discouraged and often leads to security holes.) + +**In a future major release,** React will throw an error if it encounters a `javascript:` URL. + +### Deprecating "Factory" Components {#deprecating-factory-components} + +Before compiling JavaScipt classes with Babel became popular, React had support for a "factory" component that returns an object with a `render` method: + +```js +function FactoryComponent() { + return { render() { return
; } } +} +``` + +This pattern is confusing because it looks too much like a function component — but it isn't one. (A function component would just return the `
` in the above example.) + +This pattern was almost never used in the wild, and supporting it causes React to be slightly larger and slower than necessary. So we are deprecating this pattern in 16.9 and logging a warning if it's enountered. If you rely on it, adding `FactoryComponent.prototype = React.Component.prototype` can serve as a workaround. Alternatively, you can convert it to either a class or a function component. + +We don't expect most codebases to be affected by this. + + +## New Features {#new-features} + +### Async [`act()`](/docs/test-utils.html#act) for Testing {#async-act-for-testing} + +[React 16.8](/blog/2019/02/06/react-v16.8.0.html) introduced a new testing utility called [`act()`](/docs/test-utils.html#act) to help you write tests that better match the browser behavior. For example, multiple state updates inside a single `act()` get batched. This matches how React already works when handling real browser events, and helps prepare your components for the future in which React will batch updates more often. + +However, in 16.8 `act()` only supported synchronous functions. Sometimes, you might have seen a warning like this in a test but [could not easily fix it](https://github.com/facebook/react/issues/14769): + +``` +An update to SomeComponent inside a test was not wrapped in act(...). +``` + +**In React 16.9, `act()` also accepts asynchronous functions,** and you can `await` its call: + +```js +await act(async () => { + // ... +}); +```` + +This solves the remaining cases where you couldn't use `act()` before, such as when the state update was inside an asynchronous function. As a result, **you should be able to fix all the remaining `act()` warnings in your tests now.** + +We've heard there wasn't enough information about how to write tests with `act()`. The new [Testing Recipes](/docs/testing-recipes.html) guide describes common scenarios, and how `act()` can help you write good tests. These examples use vanilla DOM APIs, but you can also use [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) to reduce the boilerplate code. Many of its methods already use `act()` internally. + +Please let us know [on the issue tracker](https://github.com/facebook/react/issues) if you bump into any other scenarios where `act()` doesn't work well for you, and we'll try to help. + +### Performance Measurements with [``](/docs/profiler.html) {#performance-measurements-with-reactprofiler} + +In React 16.5, we introduced a new [React Profiler for DevTools](/blog/2018/09/10/introducing-the-react-profiler.html) that helps find performance bottlenecks in your application. **In React 16.9, we are also adding a *programmatic* way to gather measurements** called ``. We expect that most smaller apps won't use it, but it can be handy to track performance regressions over time in larger apps. + +The `` measures how often a React application renders and what the "cost" of rendering is. Its purpose is to help identify parts of an application that are slow and may benefit from [optimizations such as memoization](/docs/hooks-faq.html#how-to-memoize-calculations). + +A `` can be added anywhere in a React tree to measure the cost of rendering that part of the tree. +It requires two props: an `id` (string) and an [`onRender` callback](/docs/profiler.html#onrender-callback) (function) which React calls any time a component within the tree "commits" an update. + +```js{2,7} +render( + + + +
+ + +); +``` + + To learn more about the `Profiler` and the parameters passed to the `onRender` callback, check out [the `Profiler` docs](/docs/profiler.html). + +> Note: +> +> Profiling adds some additional overhead, so **it is disabled in [the production build](https://reactjs.org/docs/optimizing-performance.html#use-the-production-build)**. +> +> To opt into production profiling, React provides a special production build with profiling enabled. +> Read more about how to use this build at [fb.me/react-profiling](https://fb.me/react-profiling). + +## Notable Bugfixes {#notable-bugfixes} + +This release contains a few other notable improvements: + +* A crash when calling `findDOMNode()` inside a `` tree [has been fixed](https://github.com/facebook/react/pull/15312). + +* A memory leak caused by retaining deleted subtrees [has been fixed](https://github.com/facebook/react/pull/16115) too. + +* An infinite loop caused by `setState` in `useEffect` now [logs an error](https://github.com/facebook/react/pull/15180). (This is similar to the error you see when you call `setState` in `componentDidUpdate` in a class.) + +We're thankful to all the contributors who helped surface and fix these and other issues. You can find the full changelog [below](#changelog). + +## An Update to the Roadmap {#an-update-to-the-roadmap} + +In [November 2018](/blog/2018/11/27/react-16-roadmap.html), we have posted this roadmap for the 16.x releases: + +* A minor 16.x release with React Hooks (past estimate: Q1 2019) +* A minor 16.x release with Concurrent Mode (past estimate: Q2 2019) +* A minor 16.x release with Suspense for Data Fetching (past estimate: mid 2019) + +These estimates were too optimistic, and we've needed to adjust them. + +**tldr:** We shipped Hooks on time, but we're regrouping Concurrent Mode and Suspense for Data Fetching into a single release that we intend to release later this year. + +In February, we [shipped a stable 16.8 release](/blog/2019/02/06/react-v16.8.0.html) including React Hooks, with React Native support coming [a month later](https://facebook.github.io/react-native/blog/2019/03/12/releasing-react-native-059). However, we underestimated the follow-up work for this release, including the lint rules, developer tools, examples, and more documentation. This shifted the timeline by a few months. + +Now that React Hooks are rolled out, the work on Concurrent Mode and Suspense for Data Fetching is in full swing. The [new Facebook website that's currently in active development](https://twitter.com/facebook/status/1123322299418124289) is built on top of these features. Testing them with real code helped discover and address many issues before they can affect the open source users. Some of these fixes involved an internal redesign of these features, which has also caused the timeline to slip. + +With this new understanding, here's what we plan to do next. + +### One Release Instead of Two {#one-release-instead-of-two} + +Concurrent Mode and Suspense [power the new Facebook website](https://developers.facebook.com/videos/2019/building-the-new-facebookcom-with-react-graphql-and-relay/) that's in active development, so we are confident that they're close to a stable state technically. We also now better understand the concrete steps before they are ready for open source adoption. + +Originally we thought we would split Concurrent Mode and Suspense for Data Fetching into two releases. We've found that this sequencing is confusing to explain because these features are more related than we thought at first. So we plan to release support for both Concurrent Mode and Suspense for Data Fetching in a single combined release instead. + +We don't want to overpromise the release date again. Given that we rely on both of them in production code, we expect to provide a 16.x release with opt-in support for them this year. + +### An Update on Data Fetching {#an-update-on-data-fetching} + +While React is not opinionated about how you fetch data, the first release of Suspense for Data Fetching will likely focus on integrating with *opinionated data fetching libraries*. For example, at Facebook we are using upcoming Relay APIs that integrate with Suspense. We will document how other opinionated libraries like Apollo can support a similar integration. + +In the first release, we *don't* intend to focus on the ad-hoc "fire an HTTP request" solution we used in earlier demos (also known as "React Cache"). However, we expect that both we and the React community will be exploring that space in the months after the initial release. + +### An Update on Server Rendering {#an-update-on-server-rendering} + +We have started the work on the [new Suspense-capable server renderer](/blog/2018/11/27/react-16-roadmap.html#suspense-for-server-rendering), but we *don't* expect it to be ready for the initial release of Concurrent Mode. This release will, however, provide a temporary solution that lets the existing server renderer emit HTML for Suspense fallbacks immediately, and then render their real content on the client. This is the solution we are currently using at Facebook ourselves until the streaming renderer is ready. + +### Why Is It Taking So Long? {#why-is-it-taking-so-long} + +We've shipped the individual pieces leading up to Concurrent Mode as they became stable, including [new context API](/blog/2018/03/29/react-v-16-3.html), [lazy loading with Suspense](/blog/2018/10/23/react-v-16-6.html), and [Hooks](/blog/2019/02/06/react-v16.8.0.html). We are also eager to release the other missing parts, but [trying them at scale](/docs/design-principles.html#dogfooding) is an important part of the process. The honest answer is that it just took more work than we expected when we started. As always, we appreciate your questions and feedback on [Twitter](https://twitter.com/reactjs) and in our [issue tracker](https://github.com/facebook/react/issues). + +## Installation {#installation} + +### React {#react} + +React v16.9.0 is available on the npm registry. + +To install React 16 with Yarn, run: + +```bash +yarn add react@^16.9.0 react-dom@^16.9.0 +``` + +To install React 16 with npm, run: + +```bash +npm install --save react@^16.9.0 react-dom@^16.9.0 +``` + +We also provide UMD builds of React via a CDN: + +```html + + +``` + +Refer to the documentation for [detailed installation instructions](/docs/installation.html). + +## Changelog {#changelog} + +### React {#react} + + * Add `` API for gathering performance measurements programmatically. ([@bvaughn](https://github.com/bvaughn) in [#15172](https://github.com/facebook/react/pull/15172)) +* Remove `unstable_ConcurrentMode` in favor of `unstable_createRoot`. ([@acdlite](https://github.com/acdlite) in [#15532](https://github.com/facebook/react/pull/15532)) + + ### React DOM + + * Deprecate old names for the `UNSAFE_*` lifecycle methods. ([@bvaughn](https://github.com/bvaughn) in [#15186](https://github.com/facebook/react/pull/15186) and [@threepointone](https://github.com/threepointone) in [#16103](https://github.com/facebook/react/pull/16103)) +* Deprecate `javascript:` URLs as a common attack surface. ([@sebmarkbage](https://github.com/sebmarkbage) in [#15047](https://github.com/facebook/react/pull/15047)) +* Deprecate uncommon "module pattern" (factory) components. ([@sebmarkbage](https://github.com/sebmarkbage) in [#15145](https://github.com/facebook/react/pull/15145)) +* Add support for the `disablePictureInPicture` attribute on `