反应上下文和单独的类

时间:2018-03-25 09:37:23

标签: javascript reactjs

我已经放弃了对Redux(我是React的新手)的希望,并且看到反过来的Alpha版本提供了一个新的Context。

所以我试图学习它,我的目标是,我有一个Navbar,我想在我的上下文中回复一个状态,{isAuthorised: false}

我跟随this guys video

但他所有的代码都在一个文件中。我试图做到这一点'对'。

我所做的是创建了一个名为' context'的文件夹。并在其中,创建了一个名为provider.jsx的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

在那里,我在渲染中创建了上下文(这可能是错的......也许这意味着在我的App jsx中发生?)。

我在那里创建一个状态,默认isAuthenticated为false。 (我稍后会添加代码以将其设置为应该是什么)。

这会编译......然后运行。

在我的App组件中,我使用我的提供程序:

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

所以我用MyProvider包装了所有代码。

在我的Navbar组件中,我导入了我的提供者:

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

然后我尝试在我的渲染中从我的提供者输出somethign:

     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

但这对我来说非常糟糕。

  

警告:React.createElement:type无效 - 需要一个字符串   (对于内置组件)或类/函数(对于复合   组件)但得到:未定义。你可能忘了导出你的   来自其定义的文件中的组件,或者您可能已经混淆了   默认和命名导入。

     

检查Navbar的渲染方法。       在Navbar中(由App创建)       在div(由App创建)

  

未捕获错误:元素类型无效:需要一个字符串(for   内置组件)或类/函数(用于复合组件)   但得到了:未定义。您可能忘记从中导出组件   它定义的文件,或者您可能混淆了默认和命名   进口。

我怎样才能让它发挥作用?我的.createContext应该驻留在哪里?

Code (With error) is here

3 个答案:

答案 0 :(得分:5)

问题是您导出MyProvider并尝试访问其上的静态组件 - undefined

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

Consumer作为MyContext组件中的静态属性 存在。

您需要改变的地方:

<强> 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>
        )
    }
}

然后在 navbar.jsx

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

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

看看this tutorial

修改

要让Consumer中存在MyProvider,您必须在其上指定指向MyContext Consumer的静态变量

MyProvider.Consumer = MyContext.Consumer;

然后我认为你可以使用它:

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

但是我不确定这是不是一个好主意。

答案 1 :(得分:2)

得到了同样的错误......

原来我使用的是react-dom的旧版本。将其更新为^16.3.0为我修复它!

答案 2 :(得分:0)

感谢this video使我能够正常工作。这是我的解决方法...

AuthCtrProvider.js

import React, { Component } from 'react';
// create context blank, it gets filled by provider
export const MyContext = React.createContext();
// the state object is initially in the AuthCtrProvider and gets passed into the context
// via the value argument to the provider
export default class AuthCtrProvider extends Component {
    state = {
        isAuthenticated: false,
        counter: 100,
        doubl: () => { this.setState({ counter: this.state.counter * 2 } ) }
    }

    render() {
        return (
            // pass in this.state (which carries the data and the doubl func and also
            // pass in three additional functions
            <MyContext.Provider value={{
                state: this.state,
                toggle: () => { this.setState({ isAuthenticated: !this.isAuthenticated })},
                incr: () => { this.setState({ counter: this.state.counter + 1 })},
                decr: () => { this.setState({ counter: this.state.counter - 1 })}
            }
            }>
                // include the children that will be wrapped by this provider (in App.js)
                {this.props.children}
            </MyContext.Provider>
        )
    }
}

我们导出MyContext和默认的AuthCtrProvider。这可能是不好的形式,因为我将计数器与无关的isAuthenticated混合在一起。我只是在这样做以演示功能。 还要注意-状态中有一个功能doubl,提供程序中的功能与状态分开。同样,这仅是为了进一步说明问题。

App.js

import React, { Component } from 'react';
import AuthCtrProvider, { MyContext } from './AuthCtrProvider';

class App extends Component {
    render() {
        return (
            <div className="App">
                <Navigation />
                <AuthCtrProvider>
                    <MyContext.Consumer>
                        {(context) => (
                            <p>Counter from the context = {context.state.counter}</p>
                        )}
                    </MyContext.Consumer>
                    <header className="App-header">
                        <img src={logo} className="App-logo" alt="logo" />
                        <h1 className="App-title">Welcome to React</h1>
                    </header>
                    <Main />
                </AuthCtrProvider>
            </div>
        );
    }
}

这显示了来自context.state的计数器,因此我们可以在顶层看到它。在项目中,我还有其他用于导航和路由的导入。我加载的页面之一是ProjectDetails.jsx ...

ProjectDetails.jsx

import React from 'react';
// in this file I only need to import the context
import { MyContext } from '../AuthCtrProvider';
// other imports for presentation components, OData, and such

    render() {
    return (
        <div style={{ display: "flex", flex: "1 1 0", alignItems: "flex-start", overflowY: "auto" }}>
            // we get access to the context using 'context' var but it can be whatever name you want
            <MyContext.Consumer>
                {context => (
                    <React.Fragment>
                        // note accessing the 'incr' function which is outside the state so it's just context.incr
                        <button onClick={context.incr}>incr {context.state.counter}</button>
                        // note access the 'doubl' function inside the state, hence context.state.doubl
                        <button onClick={context.state.doubl}>dubl {context.state.counter}</button>
                    </React.Fragment>
                )}
            </MyContext.Consumer>
            // other presentation going here
        </div>
    );
}

就是这样。上下文和提供程序位于单独的文件中,可以从其他文件访问数据和功能。

HTH,迈克