how useContext hook is working here with useState hook - reactjs

import { createContext, useState } from "react";
this is the place where the actual useContext data is going to be saved currentUser and setCurrentUser
export const UserContext = createContext({
currentUser: null,
seCurrentUser: () => null,
});
but here is userProvider with useState Hook and what is
the function of this useState Hook here and how Value is adding data in useState hook, if it is?
export const UserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const value = { currentUser, setCurrentUser };
return (
<UserContext.Provider value={value}> {children} </UserContext.Provider>
);
};
please tell me how it's going to be used in other web pages to collect data it's really confusing for me i'm trying to understand since last week.

this is the place where the actual useContext data is going to be saved currentUser and setCurrentUser
export const UserContext = createContext({
currentUser: null,
seCurrentUser: () => null,
});
Actually, this is just the default value. You will not be storing data here, and if a component ever gets these values it means you have forgotten to render a <UserContext.Provider>.
const [currentUser, setCurrentUser] = useState(null);
const value = { currentUser, setCurrentUser };
return (
<UserContext.Provider value={value}> {children} </UserContext.Provider>
);
This is where the work really happens. Your component has a state, which behaves just like any other state in react. The only thing different is that you are then making the current user and the setCurrentUser function available via context, so components farther down the tree can use and change the state.
Context is just a way to pass a value Component A to Component B. You still need to actually implement state or other code to do what you want.
Here's how it looks to consume the context:
const SomeComponent = () => {
const { currentUser, setCurrentUser } = useContext(UserContext);
// currentUser is the state found in UserProvider, and setCurrentUser
// is the state setter function, also from UserProvider
}

Related

infinite loop using useEffect()?

hi im getting an infinite loop with useEffect.. it seems it is not displaying any children prop instead it loops on an empty prop.
import {useEffect , createContext , useState} from "react"
import {auth} from '../../firebase'
export const AuthContext = createContext()
export const AuthContextProvider = ({children}) =>{
const [currentUser , setCurrentUser] = useState({})
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth ,(user) => {
setCurrentUser(user)
})
return ()=>{
unsubscribe()
}
}, []);
return(
<AuthContext.Provider value={{currentUser}}>
{children}
</AuthContext.Provider>
)
}
P.S this is the only code that use useEffect
Error:
react-dom.development.js:86 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

React Context API current

Okay...what is happening here cause I don't undrestand? I have an react context api where I store the current user data. In the login function I console log the currentUser and everything looks fine, but then when I write the currentUser in chrome dev tools, it appears undefined. In localStorage I also have the data. What is happening? What am I doing wrong? Can someone, please help me.
Here is my code:
authContext.js
import { createContext, useState, useEffect } from "react";
import axios from "axios";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("user")) || null
);
const login = async (inputs) => {
try {
const res = await axios.post("/login", inputs);
setCurrentUser(res.data);
console.log("res.data: ", res.data); //returns data
console.log("currentUser ", currentUser); //returns data
} catch (error) {
console.log(error);
}
};
const logout = async () => {
localStorage.clear();
setCurrentUser(null);
};
useEffect(() => {
localStorage.setItem("user", JSON.stringify(currentUser));
}, [currentUser]);
return (
<AuthContext.Provider value={{ currentUser, login, logout }}>
{children}
</AuthContext.Provider>
);
};
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import { AuthContextProvider } from "./ccontext/authContext";
import App from "./App";
import "./index.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<AuthContextProvider>
<App />
</AuthContextProvider>
</React.StrictMode>
);
Login.jsx
/*...*/
const LoginForm = () => {
const [error, setError] = useState(null);
const navigate = useNavigate();
const { login } = useContext(AuthContext);
const handleFormSubmit = (values, actions) => {
try {
login(values);
actions.resetForm();
navigate("/");
console.log("logged in");
} catch (err) {
setError(err.response.data);
}
};
/*...*/
Updating state is best seen like an asynchronous operation. You cannot set state in a function / effect and expect it to immediately be updated, on the spot. Well, it technically is, but you won't see the updates in your "already-running" function.
I am pretty sure that if you extract your log in the component root it will display the appropriate value after the login function finishes executing and the component properly sets the new state.
If you do need the value in the function you should directly use res.data.
A deeper dive:
Whenever your login function runs it forms a closure around your current values (including currentUser which is undefined at the moment).
When you update the currentUser in the login function you basically inform react that you need to update that value. It will handle this in the background, preparing the state for the next render, but your login function will keep running with whatever values it started with. Your "new" state values will not be available until you run the function again. This is because the already-running function "closed over" old values, so it can only reference those.
As a side note, if you use a ref for instance you would not have this problem. Why? Because refs do not participate in the react lifecycle. When you modify a ref it changes on the spot. You will have the updated value precisely on the next line. State does not work like that, it is coupled to the component lifecycle, so it will update on the next render.

How to use getStaticProps in combination with useReducer and useContext

I am converting a React app to a NextJS app and cannot figure out how to use the props of getStaticProps into the initial state of my reducer inside my context.
In my current setup, my AppContext looks like this:
import { createContext, useReducer } from "react";
import { initialState, appReducer } from "../reducers/appReducer";
const AppContext = createContext();
const AppContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(appReducer, initialState);
return (
<AppContext.Provider value={[state, dispatch]}>
{children}
</AppContext.Provider>
);
};
export { AppContext, AppContextProvider };
The I have my appReducer as follows:
export const initialState = {
example: null,
anotherExample: []
};
export const AppReducer = (state, action) => {
const { type, payload } = action;
switch (type) {
case "example_action":
return {...state, example: payload.example }
}
default:
return state;
}
What I do not understand is how I can get the props of getStaticProps in the initial state of my reducer (since my whole application is based on this context with reducer).
ExamplePage (pages/example-page.js)
export default function ExamplePage({ data }) {
const [state, dispatch] = useReducer(appReducer, {
...initialState,
example: data.example
});
return (
<AppContext.Provider value={[state, dispatch]}>
<div>...</div> // Rest of my application
<AppContext.Provider>
);
}
export export async function getStaticProps() {
const { data } = // Fetching my data ....
return {
props: {
data: data,
},
};
}
What I tried to do is move the reducer out of the AppContext, so I can pass the props from getStaticProps to it, but then I cannot use the AppContext including my reducer anymore in other places in my application. Or should I create my context like this:
AppContext.js
import { createContext } from "react";
const AppContext = createContext();
// Don't create my provider here, do it in ExamplePage?
export default AppContext;
And import this everywhere I want to use this context and create the provider inside my ExamplePage only?
I have the feeling I do not understand the concept of context, reducer with NextJS, is that true?
I didn't fully understand what exactly you want to achieve, but I think you're using contexts in the wrong place.
If, as I understand it, your whole React application was wrapped in a Context Provider, and you want to repeat this in nextjs, the _app.js file is best suited for this. This is a special file in nextjs, more about it here: https://nextjs.org/docs/advanced-features/custom-app, if you don't have it, you need to create it in the pages folder.
In your case it will be something like this:
// ./pages/_app.js
const AppContext = createContext();
export default function MyApp({ Component, pageProps }) {
const [state, dispatch] = useReducer(appReducer, initialState);
return(
<AppContext.Provider value={[state, dispatch]}>
<Component {...pageProps} />
</AppContext.Provider>
)
}
The _app.js is a special file in nextjs, all pages of your site will be wrapped in it, you can use this for your global states, they will not be reset when the user navigates through the pages of the site.
getStaticProps is designed for a completely different purpose, getStaticProps is designed to process data for a specific page, and not for all pages of the site.

React Context value never updates

I have the following code as a component that returns a context. For some reason when I call the updateUser function it is not setting the state, I keep getting a blank object. Is there something different I have to do to a function like that when it has parameters?
import React, { useState } from "react";
const UserContext = React.createContext({
user: {},
updateUser: (incomingUser) => {},
});
const UserData = (props) => {
const [user, setUser] = useState({});
const updateUser = (incomingUser) => {
setUser(incomingUser);
console.log(`user=${JSON.stringify(incomingUser)}`);
};
return (
<UserContext.Provider value={{ user, updateUser }}>
{props.children}
</UserContext.Provider>
);
};
export { UserData, UserContext };
Get rid of the default value for UserContext, or initialise it as null:
const UserContext = React.createContext(null);
Per the React Docs:
When React renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree.
The defaultValue argument is only used when a component does not have a matching Provider above it in the tree. This default value can be helpful for testing components in isolation without wrapping them.

useContext call preventing subsequent useState update

I have a provider wrapper around some routes
<Provider>
<Route path={ROUTES.SIGNING}><SignIn /></Route>
<PrivateRoute path={ROUTES.PRIVATE}><Private /></PrivateRoute>
</Provider>
The Provider is simply a wrapper for a userContext
import React, { useState } from 'react';
import UserContext from '../../user.context';
let defaultUser = '';
try {
defaultUser = JSON.parse(localStorage.getItem('profile'));
} catch {
defaultUser = '';
}
function Provider(props) {
const [user, setUser] = useState(defaultUser);
return <UserContext.Provider value={{ user, setUser }}>{props.children}</UserContext.Provider>
}
export default Provider;
My <SignIn /> Component waits for a response from a data service then 1. attempts to update the setter from userContext and then trys to update it's own useState function. It never seems to execute the internal useState function.
function SignIn() {
const { user, setUser } = useContext(UserContext);
const [formStatus, setFormStatus] = useState();
async function handleSubmit(e) {
e.preventDefault();
const result = await signin(credentials);
setUser({ isInternal: result.isInternal, clientId: result.clientId });
setFormStatus((curStatus) => ({ ...curStatus, state: FORMSTATUS.COMPLETED }));
}
Why would the setStatus never seem to fire? I think it's because setUser updates the Provider which is a higher level component than the child SignIn Page. Any help would be great
You have a race condition. To resolve, you need to specify that your local state update should complete before the context state update.
Solution:
Use React's functional form to set local state.
Then call your context state update within the functional set state call.
This way, you know the local state has completed before you update context.

Resources