Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Context and separate classes

I've given up hope on getting mt head around Redux (I'm new to React), and see the Alpha version of React offers a new Context.

So I am attempting to learn it, and my goal is, I have a Navbar, which I want to respond to a state within my context, {isAuthorised: false}.

I'm following this guys video:

But he has all is code in a single file. I'm trying to do it 'right'.

What I did was created a folder called 'context' and within that, created a jsx called provider.jsx.

import React, {Component} from 'react';


export default class MyProvider extends Component {
    constructor(props) {
        super(props);
        this.state = {
            isAuthenticated: false
        }
    }

    render() {
        const MyContext = React.createContext();
        return(
            <MyContext.Provider value="Test Text">
                {this.props.children}
            </MyContext.Provider>
        )
    }
}

https://github.com/CraigInBrisbane/ReactLearning/blob/master/src/context/provider.jsx

In there, I created the context within the render (This might be wrong... maybe that's meant to happen in my App jsx?).

I create a state in there, defaulting isAuthenticated to false. (I'll add code later to set that to what it should be).

This compiles... and runs.

In my App component, I use my provider like this:

import MyProvider from './context/provider.jsx';


export default class App extends Component {

    render() {
        return (
            <MyProvider>
            <div>
                <Router>
                    <div>
                    <Navbar />
                        <Route exact path='/' component={Home}  />
                        <Route path='/about' component={About} />

https://github.com/CraigInBrisbane/ReactLearning/blob/master/src/app.jsx

So I am wrapping all my code with MyProvider.

In my Navbar component, I import my provider:

import MyProvider from '../../context/provider.jsx';

I then try and output somethign from my provider within my render:

     return (
    <div>
         <MyProvider.Consumer>
            {(context)=> (
                <p>Here I am {context}</p>
            )}
        </MyProvider.Consumer> 
    <nav className="navbar navbar-expand">

https://github.com/CraigInBrisbane/ReactLearning/blob/master/src/components/navbar/navbar.jsx

But this goes very badly for me.

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of Navbar. in Navbar (created by App) in div (created by App)

And

Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

How can I get this to work? Where should my .createContext reside?

Code (With error) is here.

like image 482
Craig Avatar asked Mar 25 '18 09:03

Craig


People also ask

Can React context be used with class components?

React Context API provides first class support for class components. To use React Context, we use the Provider component to provide the shared data. In order to subscribe to the Context in class components, we use the contextType property of a class.

Can we use context consumer in class component?

Consuming Context With Class-based ComponentsOne is to use the context from Consumer like “ThemeContext. Consumer” and the other method is by assigning context object from current Context to contextType property of our class. There is always a difference in how we want to use the Context.

Can you have multiple contexts React?

To keep context re-rendering fast, React needs to make each context consumer a separate node in the tree. If two or more context values are often used together, you might want to consider creating your own render prop component that provides both.

Can we use multiple context API in React?

We can pass in anything we want to the value prop of the context provider component. So sharing multiple states with one provider is no problem.


Video Answer


2 Answers

So the problem is that you export MyProvider and try to access static component on it - which is undefined:

console.log(MyProvider.Consumer);  // undefined

the Consumer is existing as a static property only in MyContext component.

What you need to change:

provider.jsx

import React, {Component} from 'react';

export const MyContext = React.createContext();

export default class MyProvider extends Component {
    constructor(props) {
        super(props);
        this.state = {
            isAuthenticated: false
        }
    }

    render() {

        return(
            <MyContext.Provider value={this.state.isAuthenticated}>
                {this.props.children}
            </MyContext.Provider>
        )
    }
}

then in navbar.jsx

import MyProvider, { MyContext } from '../../context/provider.jsx';

<MyProvider>
   <MyContext.Consumer>
      {(context)=> (
          <p>Here I am {context}</p>
      )}
   </MyContext.Consumer> 
</MyProvider>

take a look at this tutorial.

EDIT:

To have the Consumer exist in MyProvider you would have to assign static variable on it that points to MyContext Consumer

MyProvider.Consumer = MyContext.Consumer;

Then I think you could use it like:

<MyProvider>
   <MyProvider.Consumer>
      {(context)=> (
          <p>Here I am {context}</p>
      )}
   </MyProvider.Consumer> 
</MyProvider>

However I'm not sure if it is a good idea.

like image 57
Tomasz Mularczyk Avatar answered Oct 18 '22 21:10

Tomasz Mularczyk


Was getting the same error...

Turns out I was using an older version of react-dom. Updating it to ^16.3.0 fixed it for me!

like image 38
NSjonas Avatar answered Oct 18 '22 21:10

NSjonas