我已经放弃了对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应该驻留在哪里?
答案 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>
修改强>
要让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,迈克