https://github.com/elado/react-ref-method-forwarder
Allows accessing methods of HOC-wrapped components through normal React refs
https://github.com/elado/react-ref-method-forwarder
hoc hoisting react reactjs ref
Last synced: about 2 months ago
JSON representation
Allows accessing methods of HOC-wrapped components through normal React refs
- Host: GitHub
- URL: https://github.com/elado/react-ref-method-forwarder
- Owner: elado
- Created: 2018-03-16T20:17:25.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2018-03-19T06:26:08.000Z (about 7 years ago)
- Last Synced: 2025-03-18T13:19:49.966Z (2 months ago)
- Topics: hoc, hoisting, react, reactjs, ref
- Language: JavaScript
- Homepage:
- Size: 135 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# react-ref-method-forwarder
> Allows accessing methods of HOC-wrapped components through normal React `ref`s
[](https://www.npmjs.com/package/react-ref-method-forwarder) [](https://standardjs.com)
## Install
```bash
npm install --save react-ref-method-forwarder
```
## The ProblemHTML elements and React components can have methods. `` has `focus()`, overflown elements have `scrollTo` etc.
We can use `ref`s to grab the instance and call the method:```jsx
class Field extends Component {
focusInput() {
this.input.focus()
}render() {
return (
{this.props.label}
(this.input = i)} />
)
}
}class App extends Component {
render() {
return (
(this.field = i)} />
this.field.focusInput()}>Focus
)
}
}
```There's a problem when wrapping components with HOCs, as the `ref` will now point the HOC's instance:
```jsx
class Field extends Component {
focusInput() {
this.input.focus()
}render() {
return (
{this.props.label}
(this.input = i)} />
)
}
}// this can be any HOC
const myHOC = WrappedComponent =>
class Wrapper extends Component {
render() {
return
}
}const WrappedField = myHOC(Field)
class App extends Component {
render() {
return (
(this.field = i)} />
{/* this will fail */}
this.field.focusInput()}>Focus
)
}
}
```Clicking the button will throw an error, because `WrappedField`, is an instance of `Wrapper`, which does not expose `focusInput`.
React doesn't allow `ref` forwarding. When the `Wrapper` spreads the `this.props`, the original `ref` function that was passed in the `App` `render` method will not be forwarded to `WrappedComponent`.
If `myHOC` is under our control, we could add some ref forwarding mechanism:
```jsx
const myHOC = WrappedComponent =>
class Wrapper extends Component {
render() {
const { innerRef, ...props } = this.props
return
}
}
```and use it with
```jsx
(this.field = i)} />
```This is a bit tedious, and in case `myHOC` comes from a library that cannot be modified - this solution won't work.
If some component is wrapped with multiple HOCs, they all need to talk with the same API.
`react-redux`, for example, provides `withRef` option, and exposes the wrapped component with `getWrappedInstance()`. That may not be the case in other libraries.
So there's a need for a solution that won't require change of 3rd party packages.
## The Solution / Usage
This library provides 2 HOCs: `forwardMethodsInner` and `forwardMethodsOuter`. The `forwardMethodsInner` HOC wraps the component with the methods, and the `forwardMethodsOuter` wraps the top level component. All specified methods are exposed on the result of the outer HOC.
```jsx
// Field.jsimport { forwardMethodsOuter, forwardMethodsInner } from 'react-ref-method-forwarder'
// 1st step - a Component with a method
class Field extends Component {
focusInput() {
this.input.focus()
}render() {
return (
{this.props.label}
(this.input = i)} />
)
}
}// 2nd step - wrap it with forwardMethodsInner
const FieldWrappedWithInner = forwardMethodsInner()(Field)// 3rd step - wrap it with any other HOCs, one or more
const FieldWrappedWithOtherHOCs = myHOC(FieldWrappedWithInner)// 4th step - wrap FieldWrappedWithOtherHOCs with forwardMethodsOuter
// provide all the methods that need to be hoisted
const FieldWrappedWithOuter = forwardMethodsOuter({ methods: ['focusInput'] })(FieldWrappedWithOtherHOCs)export default FieldWrappedWithOuter
// App.js
import Field from './Field'
// work with refs as if Field was never wrapped!
// focusInput is hoisted automaticallyclass App extends Component {
render() {
return (
(this.field = i)} />
this.field.focusInput()}>Focus
)
}
}
```### Usage with Decorators
```jsx
@forwardMethodsOuter({ methods: ["focusInput"] })
@myHOC
// more HOCs here
@forwardMethodsInner()
class Field extends Component {
// ...
}
```## License
MIT © [elado](https://github.com/elado)