Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React New Context API - Access Existing Context across Multiple Files

Tags:

reactjs

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.

App.js

render() {     return (         <MyProvider>             <MyConsumer />         </MyProvider>     ); } 

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

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

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?

like image 495
MattDuFeu Avatar asked Mar 21 '18 12:03

MattDuFeu


People also ask

Can you have multiple contexts in React?

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.

Can context provider have multiple values?

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.

Does New React context API trigger re renders?

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 ...


1 Answers

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>   ... 
like image 105
anthonynorton Avatar answered Oct 06 '22 20:10

anthonynorton