9 changed files with 130 additions and 26 deletions
@ -0,0 +1,22 @@ |
|||
--- |
|||
id: forwarding-refs |
|||
title: Forwarding Refs |
|||
permalink: docs/forwarding-refs.html |
|||
--- |
|||
|
|||
Ref forwarding is a technique for passing a [ref](/docs/refs-and-the-dom.html) through a component to one of its descendants. This technique can be particularly useful with [higher-order components](/docs/higher-order-components.html) (also known as HOCs). |
|||
|
|||
Let's start with an example HOC that logs component props to the console: |
|||
`embed:forwarding-refs/log-props-before.js` |
|||
|
|||
The "logProps" HOC passes all `props` through to the component it wraps, so the rendered output will be the same. For example, we can use this HOC to log all props that get passed to our "fancy button" component: |
|||
`embed:forwarding-refs/fancy-button.js` |
|||
|
|||
There is one caveat to the above example: refs will not get passed through. That's because `ref` is not a prop. Like `key`, it's handled differently by React. If you add a ref to a HOC, the ref will refer to the outermost container component, not the wrapped component. |
|||
|
|||
This means that refs intended for our `FancyButton` component will actually be attached to the `LogProps` component: |
|||
`embed:forwarding-refs/fancy-button-ref.js` |
|||
|
|||
Fortunately, we can explicitly forward refs to the inner `FancyButton` component using the `React.forwardRef` API. `React.forwardRef` accepts a render function that receives `props` and `ref` parameters and returns a React node. For example: |
|||
`embed:forwarding-refs/log-props-after.js` |
|||
|
@ -0,0 +1,15 @@ |
|||
import FancyButton from './FancyButton'; |
|||
|
|||
// highlight-next-line
|
|||
const ref = React.createRef(); |
|||
|
|||
// The FancyButton component we imported is the LogProps HOC.
|
|||
// Even though the rendered output will be the same,
|
|||
// Our ref will point to LogProps instead of the inner FancyButton component!
|
|||
// This means we can't call e.g. ref.current.focus()
|
|||
// highlight-range{4}
|
|||
<FancyButton |
|||
label="Click Me" |
|||
handleClick={handleClick} |
|||
ref={ref} |
|||
/>; |
@ -0,0 +1,12 @@ |
|||
class FancyButton extends React.Component { |
|||
focus() { |
|||
// ...
|
|||
} |
|||
|
|||
// ...
|
|||
} |
|||
|
|||
// Rather than exporting FancyButton, we export LogProps.
|
|||
// It will render a FancyButton though.
|
|||
// highlight-next-line
|
|||
export default logProps(FancyButton); |
@ -0,0 +1,26 @@ |
|||
function logProps(WrappedComponent) { |
|||
class LogProps extends React.Component { |
|||
componentWillReceiveProps(nextProps) { |
|||
console.log('old props:', this.props); |
|||
console.log('new props:', nextProps); |
|||
} |
|||
|
|||
render() { |
|||
const {forwardedRef, ...rest} = this.props; |
|||
|
|||
// Assign the custom prop "forwardedRef" as a ref
|
|||
// highlight-range{1-3}
|
|||
return ( |
|||
<WrappedComponent ref={forwardedRef} {...rest} /> |
|||
); |
|||
} |
|||
} |
|||
|
|||
// Intercept the "ref" and pass it as a custom prop, e.g. "forwardedRef"
|
|||
// highlight-range{1-3}
|
|||
function logPropsForwardRef(props, ref) { |
|||
return <LogProps {...props} forwardedRef={ref} />; |
|||
} |
|||
|
|||
return React.forwardRef(logPropsForwardRef); |
|||
} |
@ -0,0 +1,16 @@ |
|||
// highlight-next-line
|
|||
function logProps(WrappedComponent) { |
|||
class LogProps extends React.Component { |
|||
componentWillReceiveProps(nextProps) { |
|||
console.log('old props:', this.props); |
|||
console.log('new props:', nextProps); |
|||
} |
|||
|
|||
render() { |
|||
// highlight-next-line
|
|||
return <WrappedComponent {...this.props} />; |
|||
} |
|||
} |
|||
|
|||
return LogProps; |
|||
} |
@ -0,0 +1,24 @@ |
|||
function enhance(WrappedComponent) { |
|||
class Enhanced extends React.Component { |
|||
// ...
|
|||
|
|||
render() { |
|||
const {forwardedRef, ...rest} = this.props; |
|||
|
|||
// Assign the custom prop "forwardedRef" as a ref
|
|||
// highlight-range{1-3}
|
|||
return ( |
|||
<WrappedComponent ref={forwardedRef} {...rest} /> |
|||
); |
|||
} |
|||
} |
|||
|
|||
// Intercept the "ref" and pass it as a custom prop, e.g. "forwardedRef"
|
|||
// highlight-range{1-3}
|
|||
function enhanceForwardRef(props, ref) { |
|||
return <Enhanced {...props} forwardedRef={ref} />; |
|||
} |
|||
|
|||
// highlight-next-line
|
|||
return React.forwardRef(enhanceForwardRef); |
|||
} |
Loading…
Reference in new issue