With the rise of Higher Order Component composition in the React community,
there are a plethora of libraries, such as my own
redux-form
, that will add functionality to your
application by decorating your component and wrapping it in a HOC.
What you end up with is a component structure like this, where you control the
yellow components, MyContainer
and MyWrappedComponent
, but you have no
control over the red code, LibraryHOC.
Most such libraries are polite enough to pass through any props that they do not use as parameters down to your wrapped component, but sometimes they will not provide a way, e.g. getWrappedInstance, to access instance methods on your wrapped component.
What can you do? How can you get through this barrier?
Concrete Example
Let’s lay out a concrete example. Your MyWrappedComponent has instance methods of load(), save(), and clear(), and you need buttons in your MyContainer to be able to call these.
// MyContainer.jsimport React, { Component } from "react";import MyWrappedComponent from "./MyWrappedComponent";export default class MyContainer extends Component {constructor() {// bind handlersthis.handleLoad = this.handleLoad.bind(this);this.handleSave = this.handleSave.bind(this);this.handleClear = this.handleClear.bind(this);}handleLoad() {// somehow call load() on MyWrappedComponent}handleSave() {// somehow call save() on MyWrappedComponent}handleClear() {// somehow call clear() on MyWrappedComponent}render() {return (<div><button onClick={this.handleLoad}>Load</button><button onClick={this.handleSave}>Save</button><button onClick={this.handleClear}>Clear</button><MyWrappedComponent /></div>);}}
// MyWrappedComponent.jsimport React, { Component } from "react";import { hocLibrary } from "some-hoc-library";@hocLibrary({ config: "parameters" })export default class MyWrappedComponent extends Component {load() {// load something}save() {// save something}clear() {// clear something}render() {return <div>Gorgeous UI</div>;}}
How can MyContainer drill through the HOC to call methods on
MyWrappedComponent
?
Callback Drill
Because our HOC allows us to pass props down through it, we can pass a callback that will give our container component references to the wrapped component’s instance methods.
Let’s see that in action. When the inner MyWrappedComponent
is going to be
mounted, it sends up the API that it wants to expose to MyContainer
.
// MyContainer.jsimport React, { Component } from "react";import MyWrappedComponent from "./MyWrappedComponent";export default class MyContainer extends Component {constructor() {// bind handlersthis.handleCallback = this.handleCallback.bind(this);}handleCallback(load, save, clear) {// save instance methods from MyWrappedComponent to thisthis.handleLoad = load;this.handleSave = save;this.handleClear = clear;}render() {return (<div><button onClick={this.handleLoad}>Load</button><button onClick={this.handleSave}>Save</button><button onClick={this.handleClear}>Clear</button><MyWrappedComponent methodCallback={this.handleCallback} /></div>);}}
// MyWrappedComponent.jsimport React, { Component } from "react";import { hocLibrary } from "some-hoc-library";@hocLibrary({ config: "parameters" })export default class MyWrappedComponent extends Component {componentWillMount() {this.props.methodCallback(this.load.bind(this),this.save.bind(this),this.clear.bind(this));}load() {// load something}save() {// save something}clear() {// clear something}render() {return <div>Gorgeous UI</div>;}}
Conclusion
It is a little messy, but breaking through barriers often is. I have used this
pattern on several occasions, and it works well. One important side note: If
your wrapped component might be unmounted while your container remains, to
prevent a memory leak, you should “unregister” the instance methods via another
callback in componentWillUnmount()
.
Happy coding!