4

Why is this.context an empty object, in this React component lifecycle methods?

The context has the correct value in the Consumer for that context. Only the this.context API is failing.

const LoremContext = React.createContext({
    lorem: "ipsum",
})


class MenuItem extends React.Component {

    componentDidMount() {
        console.log(
            "In MenuItem.componentDidMount, this.context is:",
            this.context)
    }

    render() {
        console.log(
            "In MenuItem.render, this.context is:",
            this.context)
        return ( <LoremContext.Consumer>{
            (lorem) => {
                console.log("In LoremContext.Consumer, lorem is:", lorem)
                return (
                    <li>
                        { `Eat ${this.props.dish} at ${lorem}` }
                    </li>
                )
            }
        }</LoremContext.Consumer> )
    }
}

MenuItem.contextType = LoremContext

class Menu extends React.Component {
    render() {
        …
    }
}

class Application extends React.Component {
    render() {
        return (
            <LoremContext.Provider value={ this.props.world.lorem }>
                <section className="app">
                    <Menu menuItems={ [ … ] } />
                <p>Fusce varius id arcu egestas sodales</p>
            </section>
        </LoremContext.Provider>
    )
}

ReactDOM.render(
    <Application world={ { lorem: "consecteur" } } />,
    document.getElementById('app-container'),
)

This is using React 16.4, so it makes use of the documented context API (introduced in React 16.3).

According to that documented API, the above code should get access to the context (defined in the return value from React.createContext) in two ways:

  • The LoremContext.Consumer component receives the context value passed by the LoremContext.Provider.

    The consumer then provides that context value as an argument to the function within that component. In this case, lorem is the argument that receives the context value.

  • The this.context property receives (because of the declared MenuItem.contextType class property) the context value, inside the “lifecycle methods”.

Only one of those is working for me.

  • The LoremContext.Consumer API is getting and passing the context value correctly. The console.log output is:
  In LoremContext.Consumer, lorem is: consecteur
  • The this.context is not getting the correct value, instead it gets an empty Object. The console.log output is:
  In MenuItem.render, context is: Object {  }
  In MenuItem.componentDidMount, context is: Object {  }

So the consumer is receiving the correct value, but this.context is not. Why the difference? How can I get the correct value received at this.context?

bignose
  • 24,957
  • 12
  • 70
  • 100
  • The similar question https://stackoverflow.com/q/49809884/70157 doesn't answer this (the answers seem to assume the API won't work as documented, and provide alternative workarounds). – bignose Oct 24 '18 at 11:21

3 Answers3

5

this.context was introduced in React 16.6 that you see can see here

Before this version, on 16.4, that you are using, accessing context inside React Lifecycles can be achieved:

class Button extends React.Component {
  componentDidMount() {
    // ThemeContext value is this.props.theme
  }

  componentDidUpdate(prevProps, prevState) {
    // Previous ThemeContext value is prevProps.theme
    // New ThemeContext value is this.props.theme
  }

  render() {
    const {theme, children} = this.props;
    return (
      <button className={theme || 'light'}>
        {children}
      </button>
    );
  }
}

export default props => (
  <ThemeContext.Consumer>
    {theme => <Button {...props} theme={theme} />}
  </ThemeContext.Consumer>
);

See docs for more information

Agney
  • 13,998
  • 5
  • 36
  • 61
  • “this.context was introduced in React 16.6” — Well, crap. It'd be clearer if they *said* that in the documentation, instead of making a lot of noise about all this being new in 16.3. Thank you. – bignose Oct 24 '18 at 11:24
  • they could use a new label somewhere. But tough luck, this was added like a day or two ago – Agney Oct 24 '18 at 11:26
  • @bignose I don't see any inconsistencies here. React documentation covers the last version, which is 16.6 as of now. Context API is 16.3. context+contextType is 16.6. If you need to know where specific feature comes from, this can be figured out from a changelog or https://reactjs.org/blog/ – Estus Flask Oct 24 '18 at 11:55
1

Try creating the context in a separate file and then importing it. That worked for me. When createContext was called in the same file where the <MyContext.Provider> tag was used, the consumers only saw an empty object. When I moved createContext to a separate file, the consumers saw the expected values. This applies to both methods of consuming - <MyContext.Consumer> and MyClass.contextType/this.context.

I'm afraid I can't explain why this works, but I found the solution in this thread.

James Beninger
  • 2,168
  • 1
  • 17
  • 26
1

Unfortunatelly without this part in the target component it is empty.

static contextType = ThemeContext; // assign the correct context 

before created like

const ThemeContext = React.createContext('light');

https://reactjs.org/docs/context.html

luky
  • 1,787
  • 1
  • 17
  • 26