Skip to content

Need way to receive and pass down all context (regardless of contextTypes/childContextTypes) #8262

@moodysalem

Description

@moodysalem

Do you want to request a feature or report a bug?
Feature

What is the current behavior?
A component can only access via this.context anything defined in childContextTypes of the parent and contextTypes of the child

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).
N/A

What is the expected behavior?
Would like a way to take all the context available regardless of contextTypes and also pass down, via context, variables not defined in childContextTypes - similar to how propTypes only is for validation

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
v15.3.1

Use case:
I have a Portal component that renders its child into a div at the end of the body using ReactDOM-useful for fixed positioned components that you don't want to be in the stacking context of the parent. It works well until you have to pass down context because the context comes from an owner component not being rendered in the new HTML element

import React, { Children, PureComponent, PropTypes } from 'react';
import { unmountComponentAtNode, render } from 'react-dom';

// renders children at the end of the body
export default class Portal extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired
  };

  componentDidMount() {
    this._el = document.createElement('div');
    document.body.appendChild(this._el);

    this.renderIntoEl();
  }

  renderIntoEl = () => {
    const { children } = this.props;
    render(Children.only(children), this._el);
  };

  componentDidUpdate({ children }) {
    if (children !== this.props.children) {
      this.renderIntoEl();
    }
  }

  componentWillUnmount() {
    unmountComponentAtNode(this._el);
    document.body.removeChild(this._el);
  }

  render() {
    return null;
  }
}

I would like to be able to pass down all context available to Portal to the tree rendered in the body tag, something like this

import React, { Children, PureComponent, PropTypes } from 'react';
import { unmountComponentAtNode, render } from 'react-dom';

class ContextProvider extends PureComponent {
  static propTypes = {
    context: PropTypes.object,
    children: PropTypes.node.isRequired
  };

  getChildContext() {
    return this.props.context;
  }

  render() {
    return Children.only(this.props.children);
  }
}

// renders children at the end of the body
export default class Portal extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired
  };

  // we need to receive the context
  static contextTypes = {};

  componentDidMount() {
    this._el = document.createElement('div');
    document.body.appendChild(this._el);

    this.renderIntoEl(this.props.children, this.context);
  }

  renderIntoEl = (children, context) => {
    render(
      <ContextProvider context={context}>
        {Children.only(children)}
      </ContextProvider>,
      this._el
    );
  };

  componentWillReceiveProps({ children }, nextContext) {
    if (children !== this.props.children || nextContext !== this.context) {
      this.renderIntoEl(children, nextContext);
    }
  }

  componentWillUnmount() {
    unmountComponentAtNode(this._el);
    document.body.removeChild(this._el);
  }

  render() {
    return null;
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions