Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing React context through an HOC to a wrapped component

Is there a way that you can pass context through a React higher order component to a the component it wraps?

I have a HOC that receives context from its parent and utilizes that context to perform a basic, generalized action and then wraps a child component that also needs to access that same context to perform actions. Examples:

HOC:

export default function withACoolThing(WrappedComponent) {
  return class DoACoolThing extends Component {
    static contextTypes = {
      actions: PropTypes.object,
    }

    @autobind
    doAThing() {
      this.context.actions.doTheThing();
    }

    render() {
      const newProps = {
        doAThing: this.doAThing,
      };

      return (
        <WrappedComponent {...this.props} {...newProps} {...this.context} />
      );
    }
  }
};

Wrapped Component:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { autobind } from 'core-decorators';
import withACoolThing from 'lib/hocs/withACoolThing';


const propTypes = {
  doAThing: PropTypes.func,
};

const contextTypes = {
  actions: PropTypes.object,
};

@withACoolThing
export default class SomeComponent extends PureComponent {

  @autobind
  doSomethingSpecificToThisComponent(someData) {
    this.context.actions.doSomethingSpecificToThisComponent();
  }

  render() {
    const { actions } = this.context;

    return (
      <div styleName="SomeComponent">
        <SomeOtherThing onClick={() => this.doSomethingSpecificToThisComponent(someData)}>Do a Specific Thing</SomeOtherThing>
        <SomeOtherThing onClick={() => this.props.doAThing()}>Do a General Thing</SomeOtherThing>
      </div>
    );
  }
}

SomeComponent.propTypes = propTypes;
SomeComponent.contextTypes = contextTypes;

Passing {...this.context} in the HOC does not work. this.context is an empty {} as long as the wrapped component is wrapped by the HOC. Please help? Is there any way to pass down context that doesn't involve passing it as props??

like image 723
TheNovice Avatar asked Aug 25 '17 02:08

TheNovice


1 Answers

The Problem:

If contextTypes is not defined, then context will be an empty object.

The Solution:

Set WrappedComponent.contextTypes inside the HOC.

Explanation:

In the unfixed code, contextTypes for SomeComponent isn't being set. When SomeComponent gets decorated by @withACoolThing, any changes you make to SomeComponent are actually happening to DoACoolThing, and contextTypes for SomeComponent never gets set so it ends up being an empty object {}.

Side Note:

Because you are expanding this.context in the HOC and passing it down as props here:

<WrappedComponent {...this.props} {...newProps} {...this.context} />

You should have things like this.props.actions.doTheThing available in the child component.

like image 158
Bilbo Craggins Avatar answered Oct 12 '22 23:10

Bilbo Craggins