This document is created to overview existing React 16+ features.
Last updated: 21/03/2019
Earlier React supported only single return from the render
method:
render() {
return (
<div>
<Tab id={1} />
<Tab id={2} />
<Tab id={2} />
</div>
);
}
react@??
introduced React.Fragment
component which allows to render an array of elements without any extra wrapper element:
render() {
return (
<React.Fragment>
<Tab id={1} />
<Tab id={2} />
<Tab id={2} />
</React.Fragment>
);
}
[email protected]
allows render
method to return an array of elements:
render() {
return [
<Tab key={1} id={1} />,
<Tab key={2} id={2} />,
<Tab key={3} id={2} />,
];
}
However, returning an array from render
method has a number of downsides:
- Children in an array must be separated by commas
- Children in an array must have a key
- Strings must be wrapped in quotes
So far, [email protected]
introduced special fragment syntax:
render() {
return (
<>
<Tab id={1} />
<Tab id={2} />
<Tab id={2} />
</>
);
}
Though this syntax isn't supported very well:
[email protected]
[email protected]
+babel-eslint@7
[email protected]
still doesn't support the syntax (VS code does)
Also, if you need to pass key
property to the fragment you still have to use React.Fragment
as soon as <></>
syntax does not support passing props.
render() {
return this.definitions.map(item => (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
));
}
See the code below:
return 'now i can return strings'; // [email protected]
return undefined; // react@16 (?) - will not convert to 'undefined'
return true;
return false; // react@16 (?) - will not convert to 'false' / 'true'
[email protected]
introduces error boundaries. An error boundary is a component which defines componentDidCatch
method (legacy name is unstable_handleError
).
class ErrorBoundary {
state = {
hasError: false,
};
componentDidCatch(error, info) {
this.setState({ hasError: true });
logErrorToKibana(error, info);
}
render {
// ...
}
}
Usage:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
info
contains error metadata, e.g. info.componentStack
.
Also, as of [email protected]
if you do not define an error boundary the whole tree gets unmounted on exception in lifecycle methods.
class Tooltip {
render() {
return ReactDOM.createPortal(
<TooltipView
text={this.props.text}
position={this.props.position}
/>,
document.querySelector("#tooltips-container),
);
}
}
Event bubbling works according to vDOM, not real DOM.
[email protected]
improved support for SSR.
SSR methods such as renderToString
or renderToStaticMarkup
support same return types as React's render
method:
renderToString(<MyApp />)
renderToString([<Head /><Body />])
renderToString("render any text")
renderToString(42)
In react@15
the typical ssr-rendered html looks like this:
<div data-reactroot="" data-reactid="1"
data-react-checksum="122239856">
<!-- react-text: 2 -->This is some <!-- /react-text -->
<span data-reactid="3">server-generated</span>
<!-- react-text: 4--> <!-- /react-text -->
<span data-reactid="5">HTML.</span>
</div>
The typical html for [email protected]
is:
<div data-reactroot="">
This is some <span>server-generated</span> <span>HTML.</span>
</div>
On the client-side you have to explicitly call hydrate
instead of render
to render React app using server-side html. Also, hydration client checks become less strict. One of the most important changes:
<...> when the client-side renderer detects a markup mismatch, it only attempts to change the HTML subtree that doesn’t match, rather than the entire HTML tree.
React bundle is precompiled for both development
and production
environments:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
SSR doesn't require vDOM, as soon as it is thrown away right after rendering the html content. Omitting vDOM creation increased the render speed.
[email protected]
introduces suppressHydrationWarning
prop. It is used to suppress warnings when a content differs between client-side and server-side, e.g. timestamps.
<div suppressHydrationWarning>{Date.now()}</div>
2 new methods were introduced: renderToNodeStream
and renderToStaticNodeStream
. They return Readable
which can be easily piped to the response. This improves TTFB.
const stream = renderToNodeStream(<App />);
stream.pipe(
res, // express response object
{ end: false },
);
Vulnerability patches
Starting from [email protected]
there is a known vulnerability when using React SSR. [email protected]
fixes the issue. Also patches has been published to each minor react version containing only security fixes.
See the example of vulnerability below:
let props = {};
props[userProvidedData] = "hello";
let element = <div {...props} />;
let html = ReactDOMServer.renderToString(element);
let userProvidedData = '></div><script>alert("hi")</script>';
<div ></div><script>alert("hi")</script>
SSR, especially with streams is not that easy. Even with [email protected]
update you have to remember a lot of things, e.g.:
- Global objects are forbidden
- An error can happen in a stream. Status will be 200, though a html content will be corrupted
- Error boundaries are not supported by SSR
- React portals are not supported by SSR
- Lots of APIs differs: e.g.
fetch
In [email protected]
you can pass any attribute to the html native element. It will be passed through:
<div data-test-id="filter"><Select /></div>
<div data-test-id="filter"><!-- table html --></div>
<!-- Be aware: everything will be stringified: -->
<div data-test-id="[object object]"><!-- table html --></div>
Deprecated lifecycles:
componentWillMount => UNSAFE_componentWillMount
componentWillReceiveProps => UNSAFE_componentWillReceiveProps
componentWillUpdate => UNSAFE_componentWillUpdate
New lifecycles:
static getDerivedStateFromProps(props, state): null | stateToMergeBeforeRender
and
getSnapshotBeforeUpdate(prevProps, prevState): snapshot
componentDidUpdate(prevProps, prevState, snapshot): void
Lifecycles diagram: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
static getDerivedStateFromProps(props, state) {
if (
props.list !== state.prevPropsList ||
state.prevFilterText !== state.filterText
) {
return {
prevPropsList: props.list,
prevFilterText: state.filterText,
filteredList: props.list.filter(
item => item.text.includes(state.filterText)
)
};
}
return null;
}
The code above could be replaced with memoization:
import memoize from "memoize-one";
// ...
filter = memoize(
(list, filterText) => list.filter(item => item.text.includes(filterText))
);
render() {
const filteredList = this.filter(this.props.list, this.state.filterText);
// ...
}
}
getSnapshotBeforeUpdate(prevProps, prevState) {
return this.listRef.scrollHeight;
}
componentDidUpdate(prevProps, prevState, scrollHeight) {
this.listRef.scrollHeight = scrollHeight;
}
To help migration to new lifecycles there is a rename-unsafe-lifecycles codemod available.
[email protected]
introduces new (really exciting!) context API. See the usage example below:
// withFetch.jsx
import React from 'react';
import axios from 'axios';
const {
Provider: FetchProvider,
Consumer: FetchConsumer
} = React.createContext(axios);
export {
FetchProvider,
FetchConsumer
};
// withFetch.spec.jsx
describe('FetchConsumer / FetchProvider', () => {
it('provides axios fetch by default', (done) => {
render(
<FetchConsumer>
{(fetch) => {
expect(fetch).to.equal(axios);
done();
}}
</FetchConsumer>
);
});
it('allows to use custom fetch', (done) => {
const fetch = () => {
};
render(
<FetchProvider value={fetch}>
<FetchConsumer>
{(_fetch) => {
expect(_fetch).to.equal(fetch);
done();
}}
</FetchConsumer>
</FetchProvider>
);
});
});
As of [email protected]
there are two new methods available: React.createRef
and React.forwardRef
. See the usage example below:
class Example {
constructor() {
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.focus();
}
render() {
return <input ref={this.inputRef} />;
}
}
const Input = React.forwardRef((props, ref) => (
<input ref={ref} />
);
// ...
<Input ref={this.input} />
[email protected]
adds StrictMode
component. StrictMode
to React is same as 'use strict'
to Javascript. It ensures following things:
- No usage of
UNSAFE_
lifecycles - No usage of deprecated APIs such as string
ref
- Determinism of some lifecycles:
constructor
,render
,getDerivedStateFromProps
StrictMode
affects only development mode.
[email protected]
adds support for pointer events e.g. onPointerDown
, onPointerMove
etc.
See upcoming features to understand usage.
react + react-dom is 109 kb (34.8 kb gzipped), down from 161.7 kb (49.8 kb gzipped).
[email protected]
adds new "Profiler" tab to React DevTools. Features:
- Flame chart + changed props
- Ranked chart
- Component chart
[email protected]
introduces React.memo
HOC which is exactly the same as pure
HOC from recompose
library.
export default React.memo(props => (
<button onClick={props.onClick}>{props.title}</button>
));
[email protected]
introduces a way to dynamically load chunks. Example: https://codesandbox.io/s/jpr4o8r7ky?fontsize=14
const LazyComponent = React.lazy(() => import('./LazyComponent'));
const App = () => (
<React.Suspense fallback={<Loader />}>
<LazyComponent foo="bar" />
</React.Suspense>
)
[email protected]
introduces a convenient way to consume value from (only single) context within a class:
const MyContext = React.createContext();
class MyComponent extends React.Component {
static contextType = MyContext;
render() {
/* this.context contains context value */
}
}
[email protected]
introduces a new method to update the state in response to an error in react tree.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError => () => ({ hasError: true })
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
[email protected]
deprecated two more APIs when using StrictMode
:
- ReactDOM.findDOMNode()
- Legacy Context
[email protected]
introduces hooks.
useState
useEffect
- simple effect
- effect with unsubscribe (+ explanation of a common bug)
- single run effect ([])
- dependent effetct ([count])
useContext
Example: https://codesandbox.io/s/z23vv8lro4?fontsize=14 Recompose: https://github.com/acdlite/recompose Why recommend Hooks API: acdlite/recompose#756
Ever wonder what "async rendering" means? Here's a demo of how to coordinate an async React tree with non-React work https://t.co/3snoahB3uV pic.twitter.com/egQ988gBjR
— Andrew Clark (@acdlite) 18 сентября 2017 г.