1.1.0 • Published 6 years ago

hoist-non-react-methods v1.1.0

Weekly downloads
1,163
License
MIT
Repository
github
Last release
6 years ago

hoist-non-react-methods

Copies non-react specific methods from a child component to a parent component

build status npm version codeclimate

Inspired by @mridgway's hoist-non-react-statics

When wrapping a component (see Higher-Order Components methods that are defined in the child component aren't accessible anymore. This module makes those methods available on the wrapper component's prototype.

Installation

npm install hoist-non-react-methods --save

When is it needed?

Components can have public methods that are accessible through their instance under parent's refs.

class Composer extends React.Component {
  render() {
    return <input type="text" ref="input" />
  }

  focus() {
    return this.input.focus()
  }
}

class Root extends React.Component {
  render() {
    return (
      <div>
        <button onClick={e => this.refs.composer.focus()}></button>
        <Composer ref="composer" />
      </div>
    )
  }
}

Assuming you have a component which is decorated, the method focus will be lost, because the ref will point the decorator component.

@someDecorator()
class Composer extends React.Component {
  render() {
    return <input type="text" ref="input" />
  }

  focus() {
    return this.input.focus()
  }
}

function someDecorator() {
  return function (WrappedComponent) {
    class Wrapper extends Component {
      static displayName = `Wrapper(${WrappedComponent.displayName})`

      componentWillMount() {
        // some specific logic in a decorator
      }

      render() {
        return <WrappedComponent ref="wrappedComponent" />
      }
    }

    return Wrapper
  }
}

class Root extends React.Component {
  // this.refs.composer.focus is undefined!
  render() {
    return (
      <div>
        <button onClick={e => this.refs.composer.focus()}></button>
        <Composer ref="composer" />
      </div>
    )
  }
}

This package provides a function that copies all the methods (prototype and static) from the wrapped component to the wrapper, but keeps all react specific methods (e.g. componentDidMount etc.) untouched.

@someDecorator()
class Composer extends React.Component {
  componentWillMount() {
    // some specific behavior of Composer isn't hoisted to wrapper
  }

  render() {
    return <input type="text" ref="input" />
  }

  static someStaticMethod() {

  }

  focus() {
    return this.input.focus()
  }
}

function someDecorator() {
  return function (WrappedComponent) {
    class Wrapper extends Component {
      static displayName = `Wrapper(${WrappedComponent.displayName})`

      componentWillMount() {
        // some specific logic in a decorator, left intact
      }

      render() {
        return <WrappedComponent ref="wrappedComponent" />
      }
    }

    return hoistNonReactMethods(Wrapper, WrappedComponent, {
      delegateTo: c => c.refs.wrappedComponent
    })
  }
}

class Root extends React.Component {
  // works!
  render() {
    return (
      <div>
        <button onClick={e => this.refs.composer.focus()}></button>
        <Composer ref="composer" />
      </div>
    )
  }
}

API

hoistNonReactMethods(
  Wrapper: ReactComponent,
  WrappedComponent: ReactComponent,
  {
    delegateTo: function(ReactComponent wrapperComponentInstance):ReactComponent childComponentInstance,
    hoistStatics: boolean,
  }
)

The third parameter is a configuration object. Options:

  • delegateTo: a function that gets the instance of the wrapper component and returns the instance of the wrapped component (e.g. wrapper => wrapper.refs.child)
  • hoistStatics: true/false - controls whether to hoist statics or not

Test

npm install
npm test

License

MIT