1. Static object
To create context based on a static object, I use this code:
import React, { createContext } from 'react';
const user = {uid: '27384nfaskjnb2i4uf'};
const UserContext = createContext(user);
export default UserContext;
This code works fine.
2. Dynamic object
But if I need to create context after fetching data, I use this code:
import React, { createContext } from 'react';
const UserContext = () => {
// Let's suppose I fetched data and got user object
const user = {uid: '18937829FJnfmJjoE'};
// Creating context
const context = createContext(user);
// Returning context
return context;
}
export default UserContext;
Problem
When I debugg option 1, console.log(user) returns the object. Instead, option 2, console.log(user) returns undefined. What I'm missing?
import React, { useEffect, useState, useContext } from 'react';
import UserContext from './UserContext';
const ProjectSelector = (props) => {
const user = useContext(UserContext);
console.log(user);
return(...);
}
export default App;
one thing i would suggest is move this logic to a react component itself.
anhow you need to use a Provider in which you will set value to be the value consumers need to consume.useEffect is greatway to do asynchronous updates, like your requirment.
so , use a state variable as value of provider.in useEffect you fetch the data and update the state variable which in turn will update context value.
following is the code
UserContext.js
import { createContext } from "react";
const UserContext = createContext();
export default UserContext;
App.js
export default function App() {
const [user, setUser] = useState();
useEffect(() => {
console.log("here");
fetch("https://reqres.in/api/users/2")
.then(response => {
return response.json();
})
.then(data => {
setUser(data);
});
}, []);
return (
<div className="App">
<UserContext.Provider value={user}>
<DummyConsumer />
</UserContext.Provider>
</div>
);
}
DummyConsumer.js
import React, { useContext } from "react";
import UserContext from "./UserContext";
const DummyConsumer = () => {
const dataFromContext = useContext(UserContext);
return <div>{JSON.stringify(dataFromContext)}</div>;
};
export default DummyConsumer;
demo:anychronus context value providing
Related
I'm using a react context to manage a large input form, and I want the provider to be placed just around that input form. But it throws the following error: "A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it." This is my context:
import React from "react";
import { useState } from "react";
const AddHeaderContext = React.createContext({
headerType: "",
})
export const AddHeaderContextProvider = (props) => {
const [headerType, setHeaderType] = useState("noimage")
const headerTypeChangeHandler = (type) => {
setHeaderType(type)
}
const contextValue = {
headerType: headerType,
}
return (
<AddHeaderContext.Provider value={contextValue}>
{props.children}
</AddHeaderContext.Provider>
)
}
export default AddHeaderContext
This is when there is an error:
import AddHeaderContextProvider from './store/AddHeaderContext'
<AddHeaderContextProvider>
<AddHeaderSection />
</AddHeaderContextProvider>
But weirdly the error disappears when I move the context up into my index.js top level element and wrap everything in it.
Any idea why that could be? Also, I tap into this context usinig "useContext" hooks and not .Consumer.
I figured it out:
I just needed to use curly brackets around the contextProvider since it wasn't the main export from that file:
import { AddHeaderContextProvider } from './store/AddHeaderContext'
<AddHeaderContextProvider>
<AddHeaderSection />
</AddHeaderContextProvider>
You have this export default AddHeaderContext in AddHeaderContext.js, so when you do import AddHeaderContextProvider from './store/AddHeaderContext'
you are actually getting AddHeaderContext (the default export), not AddHeaderContextProvider.
Either change your import to import {AddHeaderContextProvider} from './store/AddHeaderContext' or your exports as below:
import React from "react";
import { useState } from "react";
export const AddHeaderContext = React.createContext({
headerType: "",
});
const AddHeaderContextProvider = (props) => {
const [headerType, setHeaderType] = useState("noimage");
const headerTypeChangeHandler = (type) => {
setHeaderType(type);
};
const contextValue = {
headerType: headerType,
};
return (
<AddHeaderContext.Provider value={contextValue}>{props.children}</AddHeaderContext.Provider>
);
};
export default AddHeaderContextProvider;
I'm trying to create a SessionProvider to avoid propdrilling and hold session globally for my components.
Im am not able to set state; menu.js L:11: setIsLoggedin is not a function
My custom hook (useSession.js):
import { useState } from 'react'
function useSession(isLoggedInState) {
const [isLoggedIn, setIsLoggedIn] = useState(isLoggedInState);
return { isLoggedIn, setIsLoggedIn, }
}
export default useSession;
My provider (SessionContext.js):
import React, { createContext, useState } from 'react'
import useSession from '../hooks/useSession'
const SessionContext = createContext();
function SessionProvider({ children, isLoggedin = false }) {
const { isLoggedIn, setIsLoggedIn } = useSession(isLoggedin);
return (
<SessionContext.Provider value={isLoggedIn, setIsLoggedIn}>{children}</SessionContext.Provider>
)
}
export { SessionProvider, SessionContext };
I've wrapped my application using in _app.js and then in Home (index.js) I try to use it there:
import { useContext } from "react";
import { SessionContext } from "../contexts/SessionContext";
export default function Menu(props) {
const { setIsLoggedIn } = useContext(SessionContext)
function onButtonClick(e) {
e.preventDefault();
setIsLoggedIn(true)
}
return (
<><span onClick={onButtonClick}>Login</span></>
)
}
Im able to read isLoggedIn, but not set it?
Your import into SessionContext should be
import useSession from '../hooks/useSession'
because you use
export default useSession;
More explaination here
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Statements/export
if you installed any linter into your ide, the errors should be underlined.
I'm updating a username based on a form input from another component. I put a console.log inside the provider component to make sure it's getting updated... it is! But the value never updates on the component receiving this value.
Here is the provider component:
import React, { useState, useContext } from 'react';
export const GetFirstName = React.createContext();
export const GetLastName = React.createContext();
export const SetUserName = React.createContext();
export const UserNameProvider = ({ children }) => {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
console.log(firstName);
return (
<SetUserName.Provider value={{ setFirstName, setLastName }}>
<GetFirstName.Provider value={firstName}>
<GetLastName.Provider value={lastName}>
{children}
</GetLastName.Provider>
</GetFirstName.Provider>
</SetUserName.Provider>
);
};
Account page (wraps the component with the provider so it can receive context):
import React, { useContext } from 'react';
import { useHistory } from 'react-router-dom';
import { GetLoggedIn, UserNameProvider } from '../Providers/providers.js';
import AccountHeader from './Account/AccountHeader.js';
import AccountItemsList from './Account/AccountItemsList.js';
import LoginModal from './Modal/LoginModal.js';
const Account = () => {
const history = useHistory();
const loggedIn = useContext(GetLoggedIn);
return !loggedIn ? (
<LoginModal closeModal={history.goBack} />
) : (
<div id='account-div'>
<UserNameProvider>
<AccountHeader />
</UserNameProvider>
<AccountItemsList /> // within AccountItemsList,
// another component is wrapped the same way
// to use setFirstName and setLastName
// this works fine, as the console.log shows
</div>
);
};
export default Account;
And finally the AccountHeader page, which receives only the initial value of '', then never reflects the current value after another component calls setFirstName.
import React, { useContext } from 'react';
import { GetFirstName } from '../../Providers/providers.js';
const AccountHeader = () => {
const firstName = useContext(GetFirstName);
return (
<div id='account-top'>
<img src='#' alt='User' />
<h1>{firstName}</h1>
</div>
);
};
Just to check my sanity I implemented a really simple version of this in a codepen and it works as it should. Elsewhere in my app I'm using context to check if the user is logged in. That is also working as it should. I've pulled almost all the hair out of my head.
There's WarriorPage component which use context where is data I want to render. After page refresh, firstly I got an empty array from the context and only after a while I got array with my data. That causes error because I'm destructuring object from that array(which is empty in the start). Any sugestions?
WarriorPage
import React, { useContext } from 'react';
import { useParams } from 'react-router-dom';
import AllWarriorsContext from '../../contexts/AllWariorsContext';
export default function WarriorPage() {
let { identy } = useParams();
const { warriorsData } = useContext(AllWarriorsContext);
const {number, name, skill, description} = warriorsData[identy]; // got undefined here after page reload
return(...);
}
In Parent component
import React, { useEffect, useState, useContext } from 'react';
import AllWarriorsContext from '../../contexts/AllWariorsContext';
import WarriorPage from '../WarriorPage/WarriorPage';
export default function Parent() {
const [myWarriorsListContext, setMyWarriorsListContext] = useState([]);
useEffect( () => {
setMyWarriorsListContext(JSON.parse(localStorage.getItem('myWarriorsList')) || []);
},[]);
return(
<AllWarriorsContext.Provider value={{
warriorsData: allWarriorsData
}}>
<WarriorPage />
</AllWarriorsContext>
);
}
I am trying to use context in my stateless component. I updated my react to v16.8.0 and added useContext, however, I keep getting these two errors and don't know what else to do. Here is my code:
import React, { useState } from "react";
import axios from "axios";
import { LanguageContext } from "./languageContext";
import { useContext } from "react";
function StripeButton() {
const context = useContext(LanguageContext);
const stripe = Stripe("pk_live_5PjwBk9dSdW7htTKHQ3HKrTd");
const [error, setError] = useState();
const handleClick = () => {
stripe
.redirectToCheckout({
...
});
};
return (
<div>
<button
id="UrgentCheckedButtonYES"
className="btn-primary"
onClick={handleClick}
>
{this.context.main.name}
<br />
</button>
<div>{error}</div>
</div>
);
}
export default StripeButton;
StripeButton.contextType = LanguageContext;
You need to import useContext like this:
import { useContext } from 'react';
const { useContext } = React
useContext is exported as method property of React
(Tested this in React 18.)
In App.js File:
import { createContext } from "react";
import ChildComponent from "./components/ChildComponent";
export const Context = createContext("Default Value");
export default function App() {
const value = "My Context Value";
return (
<Context.Provider value={value}>
<ChildComponent />
</Context.Provider>
);
}
In ChildComponent.jsx file:
import { useContext } from "react";
import { Context } from "../App";
function ChildComponent() {
const value = useContext(Context);
return <h1>{value}</h1>;
}
export default ChildComponent;
You can have as many consumers as you want for a single context. If the context value changes,then all consumers are immediately notified and re-rendered.
If the consumer isn't wrapped inside the provider, but still tries to access the context value (using useContext(Context) or <Context.Consumer>), then the value of the context would be the default value argument supplied to createContext(defaultValue) factory function that had created the context.