From a357b64503793859d9927c837a41acaec69a43bd Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 15 Mar 2018 09:42:49 -0700 Subject: [PATCH] Added React.forwardRef to 16.3 release blog post --- content/blog/2018-03-20-react-v-16-3.md | 19 +++++++++-- .../create-ref-example.js | 2 +- .../fancy-button-example.js | 32 +++++++++++++++++ .../forward-ref-example.js | 34 +++++++++++++++++++ .../hoc-theme-example.js | 10 ++++++ 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 examples/16-3-release-blog-post/fancy-button-example.js create mode 100644 examples/16-3-release-blog-post/forward-ref-example.js create mode 100644 examples/16-3-release-blog-post/hoc-theme-example.js diff --git a/content/blog/2018-03-20-react-v-16-3.md b/content/blog/2018-03-20-react-v-16-3.md index 8b336139..e6ed3a45 100644 --- a/content/blog/2018-03-20-react-v-16-3.md +++ b/content/blog/2018-03-20-react-v-16-3.md @@ -1,9 +1,9 @@ --- -title: "React v16.3.0: New lifecycles and context" +title: "React v16.3.0: New lifecycles and context API" author: [bvaughn] --- -This release includes a new class component lifecycle (`getDerivedStateFromProps`), a new `StrictMode` component, an official context API, and a new ergonomic ref API! +This release includes a new class component lifecycle (`getDerivedStateFromProps`), a new `StrictMode` component, an official context API, a new ergonomic ref API, and a ref-forwarding API! For the past few months, the React team has been working on support for [asynchronous rendering](/blog/2018/03/01/sneak-peek-beyond-react-16.html). We are excited about the new features it will enable. @@ -41,6 +41,21 @@ Version 16.3 adds a new option for managing refs that offers the convenience of [Learn more about the new `createRef` API here.](/docs/refs-and-the-dom.html) +## `forwardRef` API + +[Higher-order components](/docs/higher-order-components.html) (or HOCs) are a common way to reuse code between components. Building on the theme context example from above, we might create an HOC that injects the current "theme" as a prop: + +`embed:16-3-release-blog-post/hoc-theme-example.js` + +We can use the above HOC to wire components up to the theme context without having to use `ThemeContext` directly. For example: + +`embed:16-3-release-blog-post/fancy-button-example.js` + +HOCs typically [pass props through](/docs/higher-order-components.html#convention-pass-unrelated-props-through-to-the-wrapped-component) to the components they wrap. Unfortunately, [refs are not passed through](/docs/higher-order-components.html#refs-arent-passed-through). This means that we can't attach a ref to `FancyButton` if we use `FancyThemedButton`- so there's no way for us to call `focus()`. + +The new `forwardRef` API solves this problem by providing a way for us to intercept a `ref` and forward it as a normal prop: +`embed:16-3-release-blog-post/forward-ref-example.js` + ## Component Lifecycle Changes React's class component API has been around for years with little change. However, as we add support for more advanced features (such as [error boundaries](/docs/react-component.html#componentdidcatch) and the upcoming [async rendering mode](/blog/2018/03/01/sneak-peek-beyond-react-16.html)) we stretch this model in ways that it was not originally intended. diff --git a/examples/16-3-release-blog-post/create-ref-example.js b/examples/16-3-release-blog-post/create-ref-example.js index 77cbb40a..7589fafa 100644 --- a/examples/16-3-release-blog-post/create-ref-example.js +++ b/examples/16-3-release-blog-post/create-ref-example.js @@ -9,6 +9,6 @@ class MyComponent extends React.Component { componentDidMount() { // highlight-next-line - this.divRef.value.focus(); + this.divRef.current.focus(); } } diff --git a/examples/16-3-release-blog-post/fancy-button-example.js b/examples/16-3-release-blog-post/fancy-button-example.js new file mode 100644 index 00000000..22170e52 --- /dev/null +++ b/examples/16-3-release-blog-post/fancy-button-example.js @@ -0,0 +1,32 @@ +class FancyButton extends React.Component { + buttonRef = React.createRef(); + + focus() { + this.buttonRef.current.focus(); + } + + render() { + // highlight-next-line + const {label, theme, ...rest} = this.props; + // highlight-range{4} + return ( + + ); + } +} + +// highlight-next-line +const FancyThemedButton = withTheme(FancyButton); + +// We can render FancyThemedButton as if it were a FancyButton +// It will automatically receive the current "theme", +// And the HOC will pass through our other props. +; diff --git a/examples/16-3-release-blog-post/forward-ref-example.js b/examples/16-3-release-blog-post/forward-ref-example.js new file mode 100644 index 00000000..3c6193e0 --- /dev/null +++ b/examples/16-3-release-blog-post/forward-ref-example.js @@ -0,0 +1,34 @@ +function withTheme(Component) { + // highlight-next-line + function ThemedComponent({forwardedRef, ...rest}) { + // highlight-range{6} + return ( + + {theme => ( + + )} + + ); + } + + // Intercept the "ref" and pass it as a custom prop. + // highlight-range{1-3} + return React.forwardRef((props, ref) => ( + + )); +} + +// highlight-next-line +const fancyButtonRef = React.createRef(); + +// fancyButtonRef will now point to FancyButton +// highlight-range{4} +; diff --git a/examples/16-3-release-blog-post/hoc-theme-example.js b/examples/16-3-release-blog-post/hoc-theme-example.js new file mode 100644 index 00000000..ba0138de --- /dev/null +++ b/examples/16-3-release-blog-post/hoc-theme-example.js @@ -0,0 +1,10 @@ +function withTheme(Component) { + return function ThemedComponent(props) { + // highlight-range{2-4} + return ( + + {theme => } + + ); + }; +}