ReactJS: Problem accessing this.context in a class based consumer component - reactjs

I have a problem to access this.context in a class based consumer component. I have the following situation:
AppContext.js:
import React from "react";
const ContactContext = React.createContext(); // Create our context
export default ContactContext;
DataProvider.js:
import React, { Fragment } from "react";
import AppContext from "./AppContext";
export default class DataProvider extends React.Component {
state = {
contacts: {
contact1: {
id: 1,
firstName: 'Test User FN',
lastName: 'Test User LN'
}
}
};
render() {
return (
<>
<AppContext.Provider value={{contacts: this.state.contacts}}>
{this.props.children}
</AppContext.Provider>
</>
);
}
}
App.js:
import React from 'react';
import DataProvider from "./DataProvider";
import Contact from './components/contact/contact.component';
export default class App extends React.Component {
render() {
return (
<div>
<div className="container">
<DataProvider>
<Contact contactIndex={0}/>
</DataProvider>
</div>
</div>
);
}
}
The consumer Contact.js:
import React, { Component } from "react";
import AppContext from '../context/AppContext'
export default class Contact extends Component {
static contextType = AppContext;
componentDidMount () {
console.log('My context is: ' + this.context);
}
render() {
return (
<div className="card"></div>
);
}
}
The console output is:
My context is: undefined
Thanks for any help
Regards
Dakir

Only difference I see in the other answer's CodeSandbox is the import path is different.
import AppContext from "./AppContext";
vs:
import AppContext from '../context/AppContext'
Maybe OP has a filepath/import error?
p.s. If this is the error, TypeScript is a lifesaver for avoiding these kind of things in JS.

Your code seems right to me, I tried to replicate it in a Sandbox to find out the error and somehow works like a charm.
https://codesandbox.io/s/interesting-https-emgoz?file=/src/App.js
Tried to spot the difference but I couldn't honestly.

Related

How to use React Context API to have a state across multiple Routes?

I'm trying to understand how the context API works. I'd like to keep a global state that I can update from any Class Component. Currently, when I try to update my Context using the provided function, It only updates the value locally. In my code, I try to update a field "Day" into "Hello", and the change can be seen only when Writer is rendered. As soon as I ask my browser to render "Reader", the value is "Day" again. Why does this happen? Here's my code, I simplified it as much as I could:
index.js:
import React from "react";
import ReactDOM from "react-dom";
import {ThemeContextProvider} from "./ThemeContext";
import App from "./App";
ReactDOM.render(
<ThemeContextProvider>
<App />
</ThemeContextProvider>,
document.getElementById("root")
);
app.js:
import React from "react";
import Writer from "./Writer.js";
import Reader from "./Reader.js";
import { Context } from "./ThemeContext.js";
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
class App extends React.Component {
static contextType = Context;
constructor(props) {
super(props);
}
render() {
return (
<div className="app">
<Router>
<Switch>
<Route path="/writer" component={Writer}></Route>
<Route path="/reader" component={Reader}></Route>
</Switch>
</Router>
</div>
);
}
}
export default App;
context.js:
import React, { Component } from "react";
const Context = React.createContext();
const { Provider, Consumer } = Context
// Note: You could also use hooks to provide state and convert this into a functional component.
class ThemeContextProvider extends Component {
state = {
theme: "Day"
};
setTheme = (newTheme) => {
this.setState({theme: newTheme})
};
render() {
return <Provider value={{theme: this.state.theme, setTheme: this.setTheme}}>{this.props.children}</Provider>;
}
}
export { ThemeContextProvider, Consumer as ThemeContextConsumer, Context };
writer.js:
import React from "react";
import {Context} from "./ThemeContext";
class Writer extends React.Component {
static contextType = Context
constructor(props) {
super(props);
this.write = this.write.bind(this)
}
write () {
this.context.setTheme("hello")
}
render() {
return (
<div>
<button onClick={this.write}>press</button>
<p>{this.context.theme}</p>
</div>
);
}
}
export default Writer;
reader.js:
import React from "react";
import { Context, ThemeContextConsumer } from "./ThemeContext";
class Reader extends React.Component {
static contextType = Context;
constructor(props) {
super(props);
}
render () {
return(
<div>
<p>{this.context.theme}</p>
</div>
);}
}
export default Reader;
how do you handle the maneuver to different pages? If right now, you handle it manually by typing it directly in the search top browser input placeholder. Then it will not work since the page getting refresh. Using just context api will not make your data persistant. You need to incorporate the use of some kind of storage to make it persistant.
Anyhow, your code should work if there's not page refresh happen. To see it in different pages tho, you can and a Link (from react-router-dom package) or basically a button to redirect you to different pages, like so:-
just add this in your Writer.js component for testing purposes:-
import React from "react";
import { Link } from 'react-router-dom'
import {Context} from "./ThemeContext";
class Writer extends React.Component {
static contextType = Context
constructor(props) {
super(props);
this.write = this.write.bind(this)
}
write () {
this.context.setTheme("hello")
}
render() {
return (
<div>
<button onClick={this.write}>press</button>
<p>{this.context.theme}</p>
<Link to="/reader">Go to Reader page</Link>
</div>
);
}
}
export default Writer;

React context is undefined

This is my first time using Reactjs with Laravel. I'm trying to send data among components but the context returns as undefined. I want to send data from Product_Detail.js to Cart.js
globalContext.js
import React from "react";
export const MContext = React.createContext(); //exporting context object
export class MyProvider extends React.Component {
constructor() {
this.state = {
message: ""
};
}
render() {
return (
<MContext.Provider value={{ message: "kkk" }}>
<Cart />
<Product_Detail />
{this.props.children} //this indicates that all the child tags with
MyProvider as Parent can access the global store.
</MContext.Provider>
);
}
}
export const MyConsumer = MContext.Consumer;
Cart.js
import { MyConsumer } from "./globalContext";
<MyConsumer>{context => <p>{console.log("CCC", context)}</p>}</MyConsumer>;
export default Cart;
if (document.getElementById("shoppingCart")) {
ReactDOM.render(<Cart />, document.getElementById("shoppingCart"));
}
Product_Detail.js
import { MyConsumer } from "./globalContext";
<MyConsumer>
{context => (
<button
className="flex-c-m sizefull bg1 bo-rad-23 hov1 s-text1 trans-0-4"
onClick={() => {
console.log("kkkk", context);
}}
>
Add to Cart
</button>
)}
</MyConsumer>;
export default Product_Detail;
if (document.getElementById("product_detail")) {
ReactDOM.render(
<Product_Detail />,
document.getElementById("product_detail")
);
}
App.js
import Cart from "./components/Website/Cart";
import Product_Detail from "./components/Website/Product_Detail";
import { MyProvider } from "./components/Website/globalContext";
It's not entirely clear what you're rendering inside your app component, but I think what is going wrong is that your components are not correctly wrapped by your Provider.
Your App component should look something like this:
import React from "react";
import Cart from "./components/Website/Cart";
import Product_Detail from "./components/Website/Product_Detail";
import { MyProvider } from "./components/Website/globalContext";
const App = () => {
return (
<MyProvider>
<Cart />
<Product_Detail />
</MyProvider>
);
};
export default App;
And your globalContext like this:
import React from "react";
export const MContext = React.createContext(); //exporting context object
export class MyProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ""
};
}
render() {
return (
<MContext.Provider value={{ message: "kkk" }}>
{this.props.children}
</MContext.Provider>
);
}
}
export const MyConsumer = MContext.Consumer;
This way the value you passed to your provider get's logged out when Cart is rendered and when you click on the button rendered by Product_Detail.
Also I'm assuming that you're importing React inside the Cart and Product_Detail components and that you're correctly defining these components.

How to use 'Inject' without decorator in Mobx with ReactJs

I wonder how to use Inject without decorator.
First, I made this as Store.
//Data/imagedb
import { decorate, observable } from 'mobx';
class imageStore {
list = {
keyword : 'yoyo',
}
}
decorate(imageStore, {
list: observable,
})
export default imageStore;
Second, I made this code to inject Store on it.
//components/Image
import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
class Image extends Component {
render() {
const onPut2 = {onPut};
return (
<div>
{onPut2}
</div>
);
}
}
export default inject(({ imSt }) => ({
onPut: imSt.list.keyword,
}))(observer(Image));
And finally, this is my last code.
The error is "Onput is not defined" in second code.
import React from 'react';
import Image from 'components/Image';
import { Provider } from 'mobx-react';
import imageStore from 'Data/imagedb'
const imSt = new imageStore();
const Home = () => {
return (
<div>
<Provider imSt={imSt}>
<Image />
</Provider>
</div>
);
};
export default Home;

I face this export error of Cookie using in react

I do have this code and I'm going to use Cookies for first time but I get error below , anyone who can help me to fix the problem ?
"ERROR I FACE : Attempted import error: 'react-cookie' does not contain a default export (imported as 'Cookie'). "
import React from 'react';
import ReactDom from 'react-dom';
import './App.css';
import CountDown from './CountDown';
import Basket from './Basket';
import Cookie from 'react-cookie'
class Products extends React.Component{
constructor(props){
super(props);
this.state={
order : []
}
this.shop = this.shop.bind(this);
}
prevstate = [];
`enter code here`shop(evt){
this.prevstate.push(evt.target.id);
this.setState({
order : this.prevstate
})
console.log(Cookie.get('selected'))
Cookie.set('selected' , this.props.cart , {path :' /'});
}
render(){
return(
<div className="prowrap">
{this.props.prolist.map((name) => (
<div className="pro" key={name.id} style={{border:"1px red
solid"}} >
<img src={name.image}/>
<p>{name.name}</p>
<p>{name.detail}</p>
<p className="countdown"><CountDown time={name.date}/></p>
<div className="price">{name.price} Euro</div>
<button className="shop" id={name.id} onClick={this.shop}>Add To
Cart</button>
</div>))}
<Basket cart={this.state.order} allpro={this.props.prolist}/>
</div>
)
}
}
export default Products;
The error is clear react-cookie doesn’t have default export so you cannot import it like
import Cookie from 'react-cookie';
You need to import it like below
import { Cookies } from 'react-cookie';
Also it's not Cookie but Cookies. You are importing it wrongly
When it is default export then you don’t use {} to import but if it is not default export then you use {} to import it.
You need to import like import { withCookies, Cookies } from 'react-cookie'; and then cookies.get('selected'), refer the code below
Read the package readme carefully.
// App.jsx
import React, { Component } from 'react';
import { instanceOf } from 'prop-types';
import { withCookies, Cookies } from 'react-cookie';
import NameForm from './NameForm';
class App extends Component {
static propTypes = {
cookies: instanceOf(Cookies).isRequired
};
constructor(props) {
super(props);
const { cookies } = props;
this.state = {
name: cookies.get('name') || 'Ben'
};
}
handleNameChange(name) {
const { cookies } = this.props;
cookies.set('name', name, { path: '/' });
this.setState({ name });
}
render() {
const { name } = this.state;
return (
<div>
<NameForm name={name} onChange={this.handleNameChange.bind(this)} />
{this.state.name && <h1>Hello {this.state.name}!</h1>}
</div>
);
}
}
export default withCookies(App);

React New Context API - Access Existing Context across Multiple Files

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?
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>
...
Reading the source code of React-Context, they do
<MyContext.Provider value={{
state: this.state,
}}>
and
<MyContext.Consumer>
{(context) => <p>{context.state.age}</p>}
So if you do
<MyContext.Provider value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider>
You should get somevalue like that
<MyContext.Consumer>
{(context) => <div>{context.somevalue}</div>}
</MyContext.Consumer>
EDIT
What if you create a file called myContext.js with:
const MyContext = React.createContext('test');
export default MyContext;
and then import it like :
import MyContext form '<proper_path>/myContext';
As of right now, the two context you created in the files are not the same even thought the name is the same. You need to export the context that you created in one of the files, and use that through out.
so something like this, in your provider.js file:
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 >
);
}
}
then in your consumer.js file
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>
);
}
}
I'm gonna throw my solution into the pot - it was inspired by #Striped and simply just renames the exports into something that makes sense in my head.
import React, { Component } from 'react'
import Blockchain from './cloudComputing/Blockchain'
const { Provider, Consumer: ContextConsumer } = React.createContext()
class ContextProvider extends Component {
constructor(props) {
super(props)
this.state = {
blockchain: new Blockchain(),
}
}
render() {
return (
<Provider value={this.state}>
{this.props.children}
</Provider>
)
}
}
module.exports = { ContextConsumer, ContextProvider }
Now it's easy to implement a ContextConsumer into any component
...
import { ContextConsumer } from '../Context'
...
export default class MyComponent extends PureComponent {
...
render() {
return (
<ContextConsumer>
{context => {
return (
<ScrollView style={blockStyle.scrollView}>
{map(context.blockchain.chain, block => (
<BlockCard data={block} />
))}
</ScrollView>
)
}}
</ContextConsumer>
)
}
I'm SO done with redux!
TLDR; Demo on CodeSandbox
My current method of solving the same problem is to use the Unstated library, which as a convenient wrapper around the React Context API. "Unstated" also provides dependency injection allow the creating of discrete instances of a container; which is handy for code reuse and testing.
How to Wrap a React/Unstated-Context as a Service
The following skeleton API Service holds state properties such as loggedIn, as well as two service methods: login() and logout(). These props and methods are now available throughout the app with a single import in each file that needs the context.
For example:
Api.js
import React from "react";
// Import helpers from Unstated
import { Provider, Subscribe, Container } from "unstated";
// APIContainer holds shared/global state and methods
class APIContainer extends Container {
constructor() {
super();
// Shared props
this.state = {
loggedIn: false
};
}
// Shared login method
async login() {
console.log("Logging in");
this.setState({ loggedIn: true });
}
// Shared logout method
async logout() {
console.log("Logging out");
this.setState({ loggedIn: false });
}
}
// Instantiate the API Container
const instance = new APIContainer();
// Wrap the Provider
const ApiProvider = props => {
return <Provider inject={[instance]}>{props.children}</Provider>;
};
// Wrap the Subscriber
const ApiSubscribe = props => {
return <Subscribe to={[instance]}>{props.children}</Subscribe>;
};
// Export wrapped Provider and Subscriber
export default {
Provider: ApiProvider,
Subscribe: ApiSubscribe
}
App.js
Now the Api.js module can be used as global provide in App.js:
import React from "React";
import { render } from "react-dom";
import Routes from "./Routes";
import Api from "./Api";
class App extends React.Component {
render() {
return (
<div>
<Api.Provider>
<Routes />
</Api.Provider>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Pages/Home.js:
Finally, Api.js can subscribe to the state of the API from deep within the React tree.
import React from "react";
import Api from "../Api";
const Home = () => {
return (
<Api.Subscribe>
{api => (
<div>
<h1>🏠 Home</h1>
<pre>
api.state.loggedIn = {api.state.loggedIn ? "👍 true" : "👎 false"}
</pre>
<button onClick={() => api.login()}>Login</button>
<button onClick={() => api.logout()}>Logout</button>
</div>
)}
</Api.Subscribe>
);
};
export default Home;
Try the CodeSandbox demo here: https://codesandbox.io/s/wqpr1o6w15
Hope that helps!
PS: Someone bash me on the head quick if I'm doing this the wrong way. I'd love to learn different/better approaches. - Thanks!

Resources