I created a provider of a context where I store a name in an array that should be saved in my localstorage, but for some reason it is not saving
import React, { useEffect, useState } from "react";
import AuthContext from "./AuthContext";
const AuthProvider = ({ children }) => {
const [name, setName] = useState(() => {
const dataStorage = localStorage.getItem('name');
if (dataStorage) {
return JSON.parse(dataStorage);
} else {
return [];
}
});
useEffect(() => {
localStorage.setItem('name', JSON.stringify(name));
}, [name]);
const addName = (name) => {
setName(name);
}
return (
<AuthContext.Provider value={{ name: name, addName }}>
{children}
</AuthContext.Provider>
)
}
export default AuthProvider;
context
import { createContext } from 'react';
const AuthContext = createContext({
name: [],
addName: () => {},
});
export default AuthContext;
I'm wanting to send my name from this useState on this page to the name array inside my context and store it in localStorage
const Home = () => {
const navigate = useNavigate();
const { addName } = useContext(AuthContext);
const [name, setName] = useState('');
const [key, setKey] = useState('');
const handleEnterChat = async () => {
if (name) {
const enterName = await addName(name);
navigate('/chat')
} else {
console.log('Fill the info')
}
}
export default Home;
My handle works by sending the user to the /chat route, but it doesn't store the name in localStorage. I would like to know what is wrong with this code
Related
I'm building and app on next and node.
Trying to add authorization to admin pages, but when i try to get my auth context it returns empty for several times and after that returns the context data.
This is the auth-context.js
import React, { useCallback, useState } from "react";
import { useRouter } from "next/router";
import axios from "../lib/api/axios";
import { User } from "./types";
interface AuthState {
token: string | null;
}
interface UserState {
user: User | null;
}
type AuthContextType = {
authState: AuthState;
setAuthState: (userAuthInfo: { data: string }) => void;
isUserAuthenticated: () => boolean;
logOut: () => void;
isAdmin: () => boolean;
user: UserState;
};
const AuthContext = React.createContext<AuthContextType>({} as AuthContextType);
const { Provider } = AuthContext;
interface childrenProps {
children: React.ReactNode;
}
function setToken(data: string) {
window.localStorage.setItem("token", data);
}
function logOut() {
localStorage.removeItem("token");
}
const AuthProvider = ({ children }: childrenProps) => {
const [authState, setAuthState] = React.useState<AuthState>({
token: "",
});
const [user, setUser] = useState<UserState>({
user: null,
});
const getData = async () => {
try {
const { data } = await axios.get(`/user`);
const responseUser = data as User;
setUser({
user: responseUser,
});
} catch {
console.log("something went wrong");
}
};
const isAdmin = useCallback(() => {
return user?.user?.admin ?? false;
}, [user]);
const setUserAuthInfo = ({ data }: { data: string }) => {
setToken(data);
setAuthState({
token: data,
});
};
const isUserAuthenticated = (): boolean => {
return !authState.token ? false : true;
};
React.useEffect(() => {
if (typeof window !== undefined && window.localStorage.getItem("token")) {
const token = localStorage.getItem("token");
setAuthState({ token });
}
}, []);
React.useEffect(() => {
if (authState.token) {
const user = getData();
}
}, [authState]);
return (
<Provider
value={{
authState,
setAuthState: (userAuthInfo: { data: string }) =>
setUserAuthInfo(userAuthInfo),
isUserAuthenticated,
logOut,
isAdmin,
user,
}}
>
{children}
</Provider>
);
};
export { AuthContext, AuthProvider, logOut };
This is the control for the admin page
import { useContext, useEffect } from "react";
import { NavbarAdmin } from "../../components/adminNav";
import LayoutAdmin from "../../layout/admin";
import styles from "../../styles/Home.module.css";
import { AuthContext } from "../../lib/auth-context";
import { useRouter } from "next/router";
export default function Home() {
const {user} = useContext(AuthContext);
const router = useRouter();
useEffect(() => {
if (user.user?.admin === false || user.user === null) {
router.push("/access-denied");
}
}, [user.user?.admin]);
return (
<LayoutAdmin>
<div className={styles.container}>
<a>Name</a>
<a>Email</a>
<a>id</a>
</div>
</LayoutAdmin>
);
}
When i try to print user name etc. like this, it works flawless but when i try the codeblock on bellow. It always redirects me the access denied page.
import { useContext } from "react";
import { AuthContext } from "../lib/auth-context";
export default function aboutUs(){
// eslint-disable-next-line react-hooks/rules-of-hooks
const { user } = useContext(AuthContext);
return(
<>
<a>We are a Company!</a>
<a>{user.user?.name}</a>
</>
)
}
When i try to console log user on the admin page, this is the result.
image
This is my custom hook where I manipulate my local storage
import React from 'react';
import { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { setTaskSubmissions } from '../redux/actions/main';
function useLocalStorageHandler({setTaskSubmissions}, key, defaultValue = '') {
const [state, setState] = React.useState(() => window.localStorage.getItem(key) || defaultValue)
const prevKeyRef = React.useRef(key);
React.useEffect(() => {
const prevKey = prevKeyRef.current;
if (prevKey !== key) {
window.localStorage.removeItem(prevKey)
}
prevKeyRef.current = key
window.localStorage.setItem(key, state)
}, [key, state])
return [state, setState]
}
const mapStateToProps = state => {
return { taskSubmissions: state.main.taskSubmissions }
}
const mapDispatchToProps = {
setTaskSubmissions
}
export default connect(mapStateToProps, mapDispatchToProps)(useLocalStorageHandler);
I am using the custom hook useLocalStorageHandler in this Submission component, where I have my initialValue declared
import { useState } from 'react';
import { useLocalStorageHandler } from '../../../hooks/useLocalStorageHandler';
const Submission = ({ initialValue = '' }) => {
const [name, setName] = useLocalStorageHandler('changing the name', initialValue);
return (
<div>...
</div>)
export default Submission;
Inspite of having my curly braces in place i am facing this TypeError,
TypeError: (0 , hooks_useLocalStorageHandler__WEBPACK_IMPORTED_MODULE_15_.useLocalStorageHandler) is not a function or its return value is not iterable
I have this for saving fetched data in state:
import React, {useState, useEffect, createContext} from 'react';
import { getLocation } from '../api';
export const LocationContext = createContext();
const LocatonContextProvider = (props) => {
const [location, setLocation] = useState({})
useEffect(() => {
const fetchAPI = async () => {
setLocation(await getLocation());
}
fetchAPI();
}, [])
return (
<LocationContext.Provider value={location}>
{props.children}
</LocationContext.Provider>
);
};
export default LocatonContextProvider;
and this for saving weather data
import React, {useState, useEffect, createContext, useContext} from
'react';
//api
import { getWeather } from '../services/api';
//Context
import { LocationContext } from '../contexts/LocationContextProvider';
export const WeatherContext = createContext()
const WeatherContextProvider = ({children}) => {
const location = useContext(LocationContext);
const lat = location.lat;
const lon = location.lon;
const [weather, setWeather] = useState({});
useEffect(() => {
const fetchAPI = async () => {
setWeather(await getWeather(lat,lon));
}
fetchAPI();
}, [lat, lon])
return (
<WeatherContext.Provider value={weather}>
{children}
</WeatherContext.Provider>
);
};
export default WeatherContextProvider;
and here is the axios request:
import axios from "axios";
const getLocation = async () => {
const LOCATION_URL = 'http://ip-api.com/json/?fields=country,city,lat,lon,timezone';
const response = await axios.get(LOCATION_URL);
return response.data;
}
const getWeather = async (lat, lon) => {
const WEATHER_URL = `https://api.openweathermap.org/data/2.5/weather?
lat=${lat}&lon=${lon}&appid=bac9f71264248603c36f011a991ec5f6`;
const response = await axios.get(WEATHER_URL)
return response.data;
}
export {getLocation, getWeather};
When I refresh the page, I get an 400 error and after that I get the data, I don't know why the error occurs
useEffect(() => {
const fetchAPI = async () => {
setWeather(await getWeather(lat,lon));
}
if (lat && lon) {
fetchAPI();
}
}, [lat, lon])
I'm having problem with privateRoute it should be redirecting to the Home page after signin but it keeps redirecting to '/signin' page. everything is working fine if I remove PrivateRoute im not sure I'm beginner with react and it's my first time using firebase auth is there any way to fix this problem I'm sorry my code is messy
import React, {useEffect, useState} from 'react'
import firebase from 'firebase'
import StyledFirebaseAuth from 'react-firebaseui/FirebaseAuth'
import { Redirect, useHistory } from 'react-router-dom';
import axios from 'axios';
import { useAuth } from "../context/AuthContext";
import { Route, withRouter } from 'react-router-dom';
var uiconfig = {
signInFlow: 'popup',
signInSuccessUrl:'/',
signInOptions : [
firebase.auth.EmailAuthProvider.PROVIDER_ID],
callbacks: {
signInSuccessWithAuthResult: function(authResult, redirectUrl) {
return true;
}
}
};
const signOut = () => {
firebase.auth().signOut().then(function(){
return console.log('signout');
}).catch(() => {
console.error("error signout");
})
return <Redirect to='/signin'></Redirect>
}
var Cuser = null
const PrivateRoute = ({component: Component, ...rest}) => {
const [user, setUser] = useState(false)
const {currentUser} = useAuth()
console.log(Cuser)
return (
<Route {...rest} render={props => {
if(Cuser){
return <Component {...props}></Component>
} else {
return <Redirect to="/signin" />
}
}} ></Route>)
}
const Signup = () => {
const {history} = useHistory()
useEffect(() => {
const authOberver = firebase.auth().onAuthStateChanged((user) => {
setUser(user)
});
return authOberver
})
const [user, setUser] = useState(null)
if(user){
axios.post("http://localhost:4000/notes", {
email: user.email,
})
Cuser = true
}
return (
<>
<div>Signin / Register</div>
<StyledFirebaseAuth uiConfig={uiconfig} firebaseAuth={firebase.auth()}></StyledFirebaseAuth>
</>
)
}
export {signOut}
export default Signup;
export {PrivateRoute};
I tried to create a separate file for current user and using currentUser to check if the user is logged in or not but still happing the same problem
import React, { useContext, useEffect, useState } from "react";
import { auth } from "../firebase";
import firebase from "firebase";
import axios from 'axios'
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubscribed = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
})
return unsubscribed
}, []);
const value = {
currentUser,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
I think the problem is that you are using the variable Cuser to decide what component to render. So even if Cuser is set to true after signup, when the component gets re-rendered, it's value gets back to being null which is why it goes to else {return <Redirect to="/signin" />} block.
You can modify you AuthContext to store the login status in state. In below code, lets say cUser:
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
cont [cUser, setCUser] = useState(false);
useEffect(() => {
const unsubscribed = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
})
return unsubscribed
}, []);
const value = {
currentUser,
cUser: cUser,
setCUser: setCUser,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
And then use setCUser to set and check the value
import React, {useEffect, useState} from 'react'
import firebase from 'firebase'
import StyledFirebaseAuth from 'react-firebaseui/FirebaseAuth'
import { Redirect, useHistory } from 'react-router-dom';
import axios from 'axios';
import { useAuth } from "../context/AuthContext";
import { Route, withRouter } from 'react-router-dom';
var uiconfig = {
signInFlow: 'popup',
signInSuccessUrl:'/',
signInOptions : [
firebase.auth.EmailAuthProvider.PROVIDER_ID],
callbacks: {
signInSuccessWithAuthResult: function(authResult, redirectUrl) {
return true;
}
}
};
const signOut = () => {
firebase.auth().signOut().then(function(){
setCUser(false);
return console.log('signout');
}).catch(() => {
console.error("error signout");
})
return <Redirect to='/signin'></Redirect>
}
var Cuser = null
const PrivateRoute = ({component: Component, ...rest}) => {
const [user, setUser] = useState(false)
const {currentUser, cUser, setCUser} = useAuth()
console.log(Cuser)
return (
<Route {...rest} render={props => {
if(cUser){
return <Component {...props}></Component>
} else {
return <Redirect to="/signin" />
}
}} ></Route>)
}
const Signup = () => {
const {history} = useHistory()
useEffect(() => {
const authOberver = firebase.auth().onAuthStateChanged((user) => {
setUser(user)
});
return authOberver
})
const [user, setUser] = useState(null)
if(user){
axios.post("http://localhost:4000/notes", {
email: user.email,
})
setCUser(true);
}
return (
<>
<div>Signin / Register</div>
<StyledFirebaseAuth uiConfig={uiconfig} firebaseAuth={firebase.auth()}></StyledFirebaseAuth>
</>
)
}
export {signOut}
export default Signup;
export {PrivateRoute};
I am trying to history.push but its always undefined.
import React, { useEffect, useState } from "react";
import * as Firebase from "firebase/app";
import "firebase/auth";
import DBAPI from "./database/database-api"
import DBName from "./database/database-name"
import { useHistory } from "react-router-dom";
export const UserContext = React.createContext();
export const UserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const [isAdmin, setIsAdmin] = useState(null);
const [isVendor, setIsVendor] = useState(null);
const [pending, setPending] = useState(true);
let history = useHistory();
useEffect(() => {
Firebase.auth().onAuthStateChanged(async (user) => {
if (user != null) {
setCurrentUser(user)
let response = await Promise.all([
DBAPI.checkUserExist(DBName.admin, user.uid),
DBAPI.checkUserExist(DBName.vendor, user.uid)
]);
console.log(response[0].data)
console.log(response[1].data)
if (response[0].data) setIsAdmin(true) // admin
if (response[1].data) setIsVendor(true) // vendor
history.push(`${process.env.PUBLIC_URL}/products`)
} else {
setIsVendor(false)
setIsAdmin(false)
}
setPending(false)
});
}, []);
if(pending){
return <>Loading...</>
}
return (
<UserContext.Provider
value={{
currentUser,
isAdmin,
isVendor
}}
>
{children}
</UserContext.Provider>
);
};
The code looks ok. Just make sure your component is wrapped in a <Router> context.