Im not able to set global variable in my context provider - reactjs

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.

Related

Why Context is not sharing the data in React?

Somehow I would like to create a shared service, creating manually the provider and accessing the context works, but using serviceprovider in order to do the same and be more flexible when using multiple services doesn't seem to provide access to the inner context.
Here is my code:
app.js
import React from 'react';
import TodosContainer from './TodosContainer';
import { TodoService } from './services/TodoService';
import { createServiceContext } from './ServiceProvider';
function App() {
const { TodoServiceProvider } = createServiceContext('TodoService');
return (
<>
<TodoServiceProvider service={TodoService}>
<TodosContainer />
</TodoServiceProvider>
</>
);
}
export default App;`
here is the serviceprovider.js
import React from 'react';
export function createServiceContext(name) {
const ServiceContext = React.createContext();
function ServiceProvider({ service, children }) {
return (
<ServiceContext.Provider value={service}>
{children}
</ServiceContext.Provider>
);
}
return {
[`${name}Context`]: ServiceContext,
[`${name}Provider`]: ServiceProvider,
};
}
and here is the TodosContainer.js:
import { createServiceContext } from './ServiceProvider';
import { useContext } from 'react';
export default function TodosContainer() {
const { TodoServiceContext } = createServiceContext('TodoService');
const TodoSvc = useContext(TodoServiceContext);
result: TodoSvc is undefined ?
I've been debugging the problem since several hours, have no idea what might be wrong.
You are creating a new context every time instead of using the same one. You would have to cache them
import React from "react";
const cache = {};
export function createServiceContext(name) {
if (name in cache) {
return cache[name];
}
const ServiceContext = React.createContext();
function ServiceProvider({ service, children }) {
return (
<ServiceContext.Provider value={service}>
{children}
</ServiceContext.Provider>
);
}
const result = {
[`${name}Context`]: ServiceContext,
[`${name}Provider`]: ServiceProvider,
};
cache[name] = result;
return result;
}

Why the context provider doesn't pass props with useContext inside children as function

I'm trying to access the context props using useContext inside a children as function but the props are always the default
import React, { createContext, useContext } from "react";
const Context = createContext(undefined);
export default function Component() {
return (
<Context.Provider value={{ variable: 123 }}>
{(() => {
const context_value = useContext(Context);
console.log(context_value);
return <></>;
})()}
</Context.Provider>
);
}
Output:
undefined
I know I can use the Context.Consumer but in my real case is different because I'm creating a hook in another file to call useContext(context) instead of exporting Context
You can only use hooks (like useContext) at the top-level of function components (or in custom hooks), but not in the place where you're using it. Try to put a child component inside the provider and to put the useContext inside that child component.
this works for me, hope this will helpful to you also.
//AppContext.js
import { createContext } from 'react';
export const AppContext = createContext(null);
//Authenticated.js
import { useState } from 'react';
import { AppContext } from '#/src/AppContext';
export default function Authenticated()
{
const [message, setMessage] = useState('message');
return (
<AppContext.Provider value={{ message, setMessage }}>
<FlashMessage />
</AppContext.Provider>
);
}
//FlashMessage.js
import { useContext, useState} from 'react';
import { AppContext } from '#/src/AppContext';
const export default FlashMessage = () =>
{
const { message } = useContext(AppContext);
return (
<div>{message}</div>
);
}

Context is not available right after page refresh in React

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

createContext using a dynamic object

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

Why am I getting "useContext is not a function" or "context is not defined"?

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.

Resources