反应新的上下文API - 跨多个文件访问现有上下文

Mat*_*Feu 24 reactjs

我在React中看到的新Context API的所有示例都在一个文件中,例如https://github.com/wesbos/React-Context.

当我试图让它在多个文件中工作时,我显然错过了一些东西.

我希望创建一个GlobalConfiguration组件(MyProvider下面)在上下文中创建和管理值,为MyConsumer从中读取的任何子组件(下面)做好准备.

App.js

render() {
    return (
        <MyProvider>
            <MyConsumer />
        </MyProvider>
    );
}
Run Code Online (Sandbox Code Playgroud)

provider.js

import React, { Component } from 'react';

const MyContext = React.createContext('test');

export default class MyProvider extends Component {

    render() {
        return (
            <MyContext.Provider
                value={{ somevalue: 1 }}>
                {this.props.children}
            </MyContext.Provider >
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

consumer.js

import React, { Component } from 'react';

const MyContext = React.createContext('test');

export default class MyConsumer extends Component {

    render() {

        return (
            <MyContext.Consumer>
                {(context) => (
                    <div>{context.state.somevalue}</div>
                )}
            </MyContext.Consumer>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,在控制台中失败了:

consumer.js:12 Uncaught TypeError: Cannot read property 'somevalue' of undefined

我完全错过了这一点吗?是否有文档或示例如何跨多个文件工作?

ant*_*ton 24

我认为您遇到的问题是您正在创建两个不同的上下文,并尝试将它们用作一个上下文.它是Context通过创建React.createContext链接ProviderConsumer.

制作一个文件(我会称之为configContext.js)

configContext.js

import React, { Component, createContext } from "react";

// Provider and Consumer are connected through their "parent" context
const { Provider, Consumer } = createContext();

// Provider will be exported wrapped in ConfigProvider component.
class ConfigProvider extends Component {
  state = {
    userLoggedIn: false,                            // Mock login
    profile: {                                      // Mock user data
      username: "Morgan",
      image: "https://morganfillman.space/200/200",
      bio: "I'm Mogran—so... yeah."
    },
    toggleLogin: () => {
      const setTo = !this.state.userLoggedIn;
      this.setState({ userLoggedIn: setTo });
    }
  };

  render() {
    return (
      <Provider
        value={{
          userLoggedIn: this.state.userLoggedIn,
          profile: this.state.profile,
          toggleLogin: this.state.toggleLogin
        }}
      >
        {this.props.children}
      </Provider>
    );
  }
}

export { ConfigProvider };

// I make this default since it will probably be exported most often.
export default Consumer;
Run Code Online (Sandbox Code Playgroud)

index.js

...
// We only import the ConfigProvider, not the Context, Provider, or Consumer.
import { ConfigProvider } from "./configContext";
import Header from "./Header";
import Profile from "./Profile";

import "./styles.css";

function App() {
  return (
    <div className="App">
      <ConfigProvider>
        <Header />
        <main>
          <Profile />
        </main>
        <footer>...</footer>
      </ConfigProvider>
    </div>
  );
}
...
Run Code Online (Sandbox Code Playgroud)

Header.js

import React from 'react'
import LoginBtn from './LoginBtn'
... // a couple of styles
const Header = props => {
  return (
... // Opening tag, etc.
      <LoginBtn />  // LoginBtn has access to Context data, see file.
... // etc.
export default Header
Run Code Online (Sandbox Code Playgroud)

LoginBtn.js

import React from "react";
import Consumer from "./configContext";

const LoginBtn = props => {
  return (
    <Consumer>
      {ctx => {
        return (
          <button className="login-btn" onClick={() => ctx.toggleLogin()}>
            {ctx.userLoggedIn ? "Logout" : "Login"}
          </button>
        );
      }}
    </Consumer>
  );
};

export default LoginBtn;
Run Code Online (Sandbox Code Playgroud)

Profile.js

import React, { Fragment } from "react";
import Consumer from "./configContext"; // Always from that same file.

const UserProfile = props => {...}; // Dumb component

const Welcome = props => {...}; // Dumb component

const Profile = props => {
  return (
    <Consumer>
      ...
        {ctx.userLoggedIn ? (
          <UserProfile profile={ctx.profile} />
        ) : (<Welcome />)}
      ...
    </Consumer>
  ...
Run Code Online (Sandbox Code Playgroud)

  • 只是给道具。它应该已经被接受的答案。 (3认同)

Str*_*ped 11

阅读React-Context的源代码,就可以了

<MyContext.Provider value={{
  state: this.state,
}}>
Run Code Online (Sandbox Code Playgroud)

<MyContext.Consumer>
  {(context) => <p>{context.state.age}</p>}
Run Code Online (Sandbox Code Playgroud)

所以,如果你这样做

<MyContext.Provider value={{ somevalue: 1 }}>
  {this.props.children}
</MyContext.Provider>
Run Code Online (Sandbox Code Playgroud)

你应该somevalue那样

<MyContext.Consumer>
  {(context) => <div>{context.somevalue}</div>}
</MyContext.Consumer>
Run Code Online (Sandbox Code Playgroud)

编辑

如果您创建一个名为的文件myContext.js,该怎么办:

const MyContext = React.createContext('test');
export default MyContext;
Run Code Online (Sandbox Code Playgroud)

然后导入它像:

import MyContext form '<proper_path>/myContext';

  • 这会使MyContext有效地成为全局变量吗?我认为这不会与`let SOME_VALUE = 1; 在global.js文件中导出默认值SOME_VALUE;`并将其导入到处? (2认同)

Wen*_*n W 5

截至目前,即使名称相同,您在文件中创建的两个上下文也不相同。您需要导出您在其中一个文件中创建的上下文,并始终使用它。

所以像这样,在你的 provider.js 文件中:

import React, { Component } from 'react';

const MyContext = React.createContext();
export const MyContext;

export default class MyProvider extends Component {
    render() {
        return (
            <MyContext.Provider
                value={{ somevalue: 1 }}>
                {this.props.children}
            </MyContext.Provider >
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的 consumer.js 文件中

import MyContext from 'provider.js';
import React, { Component } from 'react';
export default class MyConsumer extends Component {
    render() {
        return (
            <MyContext.Consumer>
                {(context) => (
                    <div>{context.somevalue}</div>
                )}
            </MyContext.Consumer>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)