简体   繁体   中英

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.

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';

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM