All the examples I've seen of the new Context API in React are in a single file, e.g. https://github.com/wesbos/React-Context.
When I try to get it working across multiple files, I'm clearly missing something.
I'm hoping to make a GlobalConfiguration
component (the MyProvider
below) create and manage the values in the context, ready for any child component (MyConsumer
below) read from it.
render() { return ( <MyProvider> <MyConsumer /> </MyProvider> ); }
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 > ); } }
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> ); } }
Unfortunately that fails with this in the console:
consumer.js:12 Uncaught TypeError: Cannot read property 'somevalue' of undefined
Have I completely missed the point? Is there documentation or an example of how this works across multiple files?
Consuming Multiple Contexts 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.
The React Context Provider component accepts a value prop as input. We can pass anything we want to the value prop. In other words, React Context can easily handle multiple values.
The updates to context values doesn't trigger re-render for all the children of the provider, rather only components that are rendered from within the Consumer, so in your case although number component contains the Consumer, Number component isn't re-rendered, rather just the render function within the Consumer and ...
I think the problem that you are running into is that you are creating two different contexts, and trying to use them as one. It is the Context
created by React.createContext
that links Provider
and Consumer
.
Make a single file (I'll call it 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;
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> ); } ...
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
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;
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> ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With