Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React 16.3 Context API -- Provider/Consumer issues

I have been doing some experiment on React 16.3.1 ContextAPI. and I encountered into something that I couldn't fathom. I was hoping I could use your help.

Note: The problem have been solved but, its not the solution I am looking for.

Let start with first experiment on multiple components within same file Index.js.

import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();

class AppProvider extends Component {
  state = {
    name: 'Superman',
    age: 100
  };

  render() {
    const increaseAge = () => {
      this.setState({ age: this.state.age + 1 });
    };

    const decreaseAge = () => {
      this.setState({ age: this.state.age - 1 });
    };
    return (
      <Provider
        value={{
          state: this.state,
          increaseAge,
          decreaseAge
        }}
      >
        {this.props.children}
      </Provider>
    );
  }
}

class Person extends Component {
  render() {
    return (
      <div className="person">
        <Consumer>
          {context => (
            <div>
              <p>I'm {context.state.name}</p>
              <p>I'm {context.state.age}</p>
              <button onClick={context.increaseAge}>
                <span>+</span>
              </button>
              <button onClick={context.decreaseAge}>
                <span>-</span>
              </button>
            </div>
          )}
        </Consumer>
      </div>
    );
  }
}

class App extends Component {
  render() {
      return (
        <AppProvider>
          <div className="App">
            <p>Imma Apps</p>
            <Person />
          </div>
        </AppProvider>
      );
    }
  }

export default App;

As result, this render out perfect without any error. I am able to see name (Superman) and age (100). I am able to increase and decrease age by 1.

As you can see, I have imported {createContext} from react then created {Provider, Consumer}. Wrapped <Provider> with state value and <Consumer>.

Next Experiment, was exact copy each component from index.js and paste them separately into their own files.

AppProvider.js

import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();

class AppProvider extends Component {
  state = {
    name: 'Superman',
    age: 100
  };

  render() {
    const increaseAge = () => {
      this.setState({ age: this.state.age + 1 });
    };

    const decreaseAge = () => {
      this.setState({ age: this.state.age - 1 });
    };
    return (
      <Provider
        value={{
          state: this.state,
          increaseAge,
          decreaseAge
        }}
      >
        {this.props.children}
      </Provider>
    );
  }
}
export default AppProvider;

Person.js

import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();

class Person extends Component {
  render() {
    return (
      <div className="person">
        <Consumer>
          {context => (
            <div>
              <p>I'm {context.state.name}</p>
              <p>I'm {context.state.age}</p>
              <button onClick={context.increaseAge}>
                <span>+</span>
              </button>
              <button onClick={context.decreaseAge}>
                <span>-</span>
              </button>
            </div>
          )}
        </Consumer>
      </div>
    );
  }
}
export default Person;

App.js

import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();

class App extends Component {
  render() {
      return (
        <AppProvider>
          <div className="App">
            <p>Imma Apps</p>
            <Person />
          </div>
        </AppProvider>
      );
    }
  }

export default App;

As result, I am getting error - TypeError: Cannot read property 'state' of undefined.

I am unable to grasp what the exactly error was.. All I did was copy and paste each into files without changing any syntax.

Although, Alternative method was to create a new file and add syntax following...

Context.js

import { createContext } from 'react';
const Context = createContext();
export default Context;

Then go into each files (AppProvider.js. Person.js and App.js) and replace...

import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();'

...into...

import Context from './Context.js';. Also replace... <Provider> into <Context.Provider> and <Consumer> into <Context.Consumer>.

And this killed the error. However, this is not the solution I am looking for. I wanted to use <Provider> tag instead of <Context.Provider>.

Question is, Why am I getting this error?

Why am I unable to use this method...

import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();'

for each components in separate files so I could use <Provider> tag ?

Are there any way around to get the solution I'm looking for?

Your help is appreciated and Thanks in advance.

like image 296
sirrus Avatar asked Apr 07 '18 15:04

sirrus


People also ask

What's new in react 16 3?

Well, with the release of React 16.3, we're finally getting a stable context API and what's even better is that it has received a makeover and the dev experience is fantastic! In this lesson, we'll look at an example app that passes props down several levels deep into the component tree and replace all that prop drilling with the new context API.

What is provider in react context?

Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes. The Provider component accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers.

What is Context API in react?

Context api in React.js. The React context api is safe to use in production with the version 16.3 or latest. The reason for adding context api is to avoid the passing of props if there is a chain of children components. Without the use of context api, we have to pass the props through all the intermediate components.

How do I update the context of a component in react?

It is often necessary to update the context from a component that is nested somewhere deeply in the component tree. In this case you can pass a function down through the context to allow consumers to update the context: To keep context re-rendering fast, React needs to make each context consumer a separate node in the tree.


1 Answers

Your are getting TypeError: Cannot read property 'state' of undefined. Beacuse every time you call const { Provider, Consumer } = createContext(); it creates a new object, this object need to be exported in order for consumers to consume this specific object.

So in person.js when you try doing {context.state.age} it really does not have state on this object, you just created a new Context which is empty or rather with React internal methods and properties.

So in order to consume the same object just export it, like you did in Context.js and instead of doing:

import { createContext } from 'react';
const Context = createContext();
export default Context;

replace to:

import { createContext } from 'react';
const { Provider, Consumer } = createContext();
export { Consumer, Provider };

Then when you want to use it in other files ( meaning import it ) just call:

import { Consumer, Provider } from './Context.js';
like image 58
Boaz Hoch Avatar answered Oct 19 '22 11:10

Boaz Hoch