I'm trying to control the page using react-hook, react-router-dom, redux.
The login screen is implemented and the code that tries to switch to the main page when the login is successful is written.
So I used history.push of react-router.
However, history.push only changes the browser url, not the actual information displayed on the page.
The code is shown below.
In brief code explanation,
The id and password are put into redux and get as getelementbyuId and sent as form data.
The login function works normally.
history.push ('/') written to "userUpdateUserInfo" doesn't work at all.
Only the url of the browser changes, not the main page.
App.tsx
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Main, Login } from './pages';
import './css/App.css';
const App: React.FC = () => {
return (
<div>
<div className="App-contents-area">
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/main" component={Main} />
{/* <Redirect path="*" to="/" /> */}
</Switch>
</div>
</div>
);
}
export default App;
LoginPage.tsx
import React from 'react';
import { Login } from 'Component';
function LoginPage() {
return (
<Login />
);
}
export default LoginPage;
Login.tsx (components)
import React from 'react';
import {
LoginTitle, LoginAvatar, LoginUserId, LoginUserPassword, LoginButton
} from '..';
import '../../css/Login.css';
function Login() {
return (
<div className="Login">
<div className="Login-form-data">
<LoginTitle /> // code skip
<LoginAvatar /> // code skip
<LoginUserId /> // code skip
<LoginUserPassword /> // code skip
<LoginButton />
</div>
</div>
);
}
export default Login;
LoginButton.tsx (components)
import React from 'react';
import { useUpdateUserInfo } from 'Hook';
function LoginButton() {
const { handleLogin } = useUpdateUserInfo(); // custom hook
return (
<div className="LoginButton">
<button className="LoginButton-button" onClick={handleLogin}>Login</button>
</div>
);
}
export default LoginButton;
userUpdateUserInfo.tsx (custom hook)
import { useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from 'Store/modules';
import { updateUserInfo } from 'Store/modules/user';
import { userLoginStatus } from 'Store/modules/login';
import { msg } from 'Lang';
import {
axiosPost, history,
_ShowFail, _ShowSuccess, _ShowSelect
} from 'Module';
export default function useUpdateUserInfo () {
const { id, name, tel, email } = useSelector((state: RootState) => state.user);
let { isLogin } = useSelector((state: RootState) => state.login);
const dispatch = useDispatch();
const handleLogin = useCallback(async () => {
try {
const userId: string = (document.getElementById('LoginUserId-id') as HTMLInputElement).value.trim();
const userPw: string = (document.getElementById('LoginUserPassword-password') as HTMLInputElement).value.trim();
if (!userId.length) { return _ShowFail(msg.pleaseInputUserId); }
if (!userPw.length) { return _ShowFail(msg.pleaseInputUserPassword); }
const formData: FormData = new FormData();
formData.append('userId', userId);
formData.append('userPw', userPw);
const url = '/login/check-login-info';
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
};
const res = await axiosPost(url, formData, config);
if (res.data.res) {
_ShowSuccess('login success');
const userInfo = {
id: res.data.rows[0].id,
name: res.data.rows[0].name,
email: res.data.rows[0].email,
tel: res.data.rows[0].tel,
};
isLogin = true;
/**************************/
history.push('/main'); // now working
/**************************/
dispatch(updateUserInfo(userInfo));
dispatch(userLoginStatus({ isLogin }));
}
else {
_ShowFail('login fail');
isLogin = false;
dispatch(updateUserInfo({ id, email, name, tel }));
dispatch(userLoginStatus({ isLogin }));
}
}
catch (error) {
_ShowFail(error.message);
}
}, [dispatch]);
return { handleLogin };
};
MainPage.tsx
import React from 'react';
function MainPage() {
return (
<div>
<h2>MainPage!!</h2>
</div>
);
}
export default MainPage;
history.tsx
import { createBrowserHistory } from 'history'
export default createBrowserHistory();
Since last night, I have been suffering from this problem.
How can I change the content of the page?
In App.js your Routes suppose to be inside < Router > < /Router >,
https://reacttraining.com/react-router/web/api/Router
(unless you wrap it on index.js that not including here and App is imported inside there)?
Related
I'm coding an app that requires you to login using Laravel/Sanctum as the backend (no JWT). I'm using localStorage to persist the user's login details in case the browser page is reloaded, I can then re-apply state in my useAuthHandler hook.
This works fine, but I would like to either:
a) Replace the localStorage with an Axios request to ensure I re-apply state with genuine login data from the backend (i.e. the user did not forge localStorage details) before rendering the app.
--- OR ---
b) Keep using localStorage, but send an Axios request back to the server to confirm the localStorage details were indeed valid before rendering any of the app.
With the following code, I'm having trouble making the Axios request synchronous in the getStoredAuth() function where the localStorage is fetched. I need to avoid having my app render before the request has returned. This is so I can avoid any page 'flash' while auth details are being checked.
AuthContext.js
import useAuthHandler from "../utils/custom-hooks/AuthHandler";
import { getStoredUserAuth } from "../helpers/Helpers";
export const authContext = createContext();
const { Provider } = authContext;
const AuthProvider = ({ children }) => {
const { isAuthenticated, user, setAuthStatus, setUnauthStatus } = useAuthHandler(
getStoredUserAuth()
);
return (
<Provider value={{ isAuthenticated, user, setAuthStatus, setUnauthStatus }}>
{children}
</Provider>
);
};
export default AuthProvider;
AuthHandler.js
import { useState } from "react";
import { DEFAULT_USER_AUTH } from "../Consts";
const useAuthHandler = (initialState) => {
const [isAuthenticated, setIsAuthenticated] = useState(initialState.isAuthenticated);
const [user, setUser] = useState(initialState.user);
const setAuthStatus = (userAuth) => {
window.localStorage.setItem("UserAuth", JSON.stringify(userAuth));
setIsAuthenticated(userAuth.isAuthenticated);
setUser(userAuth.user);
};
const setUnauthStatus = () => {
window.localStorage.clear();
setIsAuthenticated(DEFAULT_USER_AUTH.isAuthenticated);
setUser(DEFAULT_USER_AUTH.user);
};
return {
isAuthenticated,
user,
setAuthStatus,
setUnauthStatus,
};
};
export default useAuthHandler;
Helpers.js
import { DEFAULT_USER_AUTH } from "../utils/Consts";
/** Return user auth from local storage value */
export const getStoredUserAuth = () => {
const auth = window.localStorage.getItem("UserAuth");
if (auth) {
/**
* Axios synchronous check with server if login is still valid here
* before returning any data.
*
* If the localStorage data is invalid, or the server's session has
* expired, then redirect to /login before rendering anything.
*/
return JSON.parse(auth);
}
return DEFAULT_USER_AUTH;
};
Consts.js
export const DEFAULT_USER_AUTH = { isAuthenticated: false, user: {} };
App.js
import { IonApp, IonRouterOutlet, setupIonicReact } from '#ionic/react';
import { IonReactRouter } from '#ionic/react-router';
import { Redirect, Route, Switch } from 'react-router-dom';
import routes from './routes';
import { useState, useEffect } from 'react';
import { Content, Page } from "./components/IonComponents";
import { useSelector } from "react-redux";
import Menu from "./components/Menu/Menu";
import { PrivateRoute } from "./components/Menu/PrivateRoute";
import AuthContextProvider from "./contexts/AuthContext";
/* Core CSS required for Ionic components to work properly */
import '#ionic/react/css/core.css';
/* Stylesheets */
import './assets/scss/variables.scss';
import "bootstrap/dist/css/bootstrap.min.css";
import "./assets/scss/propeller-styles.scss";
setupIonicReact();
const App = () => {
const [displayMenuExpanded, setDisplayMenuExpanded] = useState(false);
const menuExpanded = useSelector((state) => state.menuExpanded.value);
useEffect(() => {
setDisplayMenuExpanded(menuExpanded);
}, [menuExpanded]);
return (
<IonApp>
<IonReactRouter basename={process.env.PUBLIC_URL}>
<AuthContextProvider>
<Menu expanded={displayMenuExpanded} />
<IonRouterOutlet
className={displayMenuExpanded ? "router-narrow" : "router-expanded"}
>
<Switch>
<>
<Page>
<Content
className={
displayMenuExpanded ? "content-narrow" : "content-expanded"
}
>
<div id="content-wrapper">
<Route exact path="/">
<Redirect to="/utilities" />
</Route>
{routes.map((route, key) => (
<PrivateRoute
key={key}
path={route.pathname}
exact
component={route.component}
/>
))}
</div>
</Content>
</Page>
</>
</Switch>
</IonRouterOutlet>
</AuthContextProvider>
</IonReactRouter>
</IonApp >
);
};
export default App;
PrivateRoute.js
import { useContext } from 'react'
import { Route, Redirect } from 'react-router-dom';
import { authContext } from "../../contexts/AuthContext";
export { PrivateRoute };
function PrivateRoute({ component: Component, ...rest }) {
const authCtx = useContext(authContext);
if (!Component) return null;
return (
<Route {...rest} render={props => {
if (!authCtx.isAuthenticated && props.location.pathname !== "/login") {
// Not logged-in so redirect to login page with the return url
return <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
}
// Authorised so return component
return <Component {...props} />
}} />
);
}
The above PrivateRoute component is how I redirect the user if they're not authenticated.
Thank you for your help.
I am trying to use context with my Gatsby project. I have successfully implemented this in my previous project and I have copied the code over to my new project and it's not working as intended.
This is my context.js file:
import React, { useContext, useState } from "react";
const defaultState = {
isLoggedIn: false,
};
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
function toggle() {
console.log("BOO!");
}
const value = {
isLoggedIn,
setIsLoggedIn,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
This is my app.js file:
import React from "react";
import { Router } from "#reach/router";
import IndexPage from "./index";
import ProjectPage from "./project";
import { AuthProvider } from "../contexts/context";
const App = () => (
<AuthProvider>
<Router basepath="/app">
<IndexPage path="/" component={IndexPage} />
<ProjectPage path="/project" component={ProjectPage} />
</Router>
</AuthProvider>
);
export default App;
This is my index.js file:
import React, { useContext } from "react";
import { Link } from "gatsby";
import { useAuth } from "../contexts/context";
import { AuthContext } from "../contexts/context";
const IndexPage = () => {
console.log(useAuth())
return (
<div className="w-40 h-40 bg-red-400">
{/*<Link to="/project">to projects</Link>*/}
<div>Click me to toggle: uh</div>
</div>
);
};
export default IndexPage;
useAuth() should return the desired components and functions but instead is always returning undefined. I have looked over my previous code as well as snippets on stack overflow and I can't seem to find the correct fix.
The following includes code that successfully built and executed:
Original context.js
import '#stripe/stripe-js'
/* Functionality */
import React, { useContext, useEffect, useState } from "react";
import { navigate } from "#reach/router";
import firebase from 'gatsby-plugin-firebase';
import { useLocalStorage } from 'react-use';
const AuthContext = React.createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setIsLoading] = useLocalStorage("loading", false);
// Sign In
const signInWithRedirect = (source) => {
let provider;
switch(source) {
case 'Google':
provider = new firebase.auth.GoogleAuthProvider()
break;
case 'Github':
provider = new firebase.auth.GithubAuthProvider()
break;
default:
break;
}
setIsLoading(true)
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(() => {
// Existing and future Auth states are now persisted in the current
// session only. Closing the window would clear any existing state even
// If a user forgets to sign out.
// ...
// New sign-in will be persisted with session persistence.
return firebase.auth().signInWithRedirect(provider)
})
.catch((error) => {
// Handle Errors here.
let errorCode = error.code;
let errorMessage = error.message;
});
}
// Sign Out
const signOut = () => {
firebase.auth().signOut().then(() => {
// Sign-out successful.
setIsLoggedIn(false)
navigate('/app/login')
}).catch((error) => {
// An error happened.
});
}
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
try {
// If user is authenticated
if (!!user) {
// Fetch firestore document reference
var docRef = firebase.firestore().collection("study_guide_customers").doc(user.uid)
docRef.get().then((doc) => {
console.log('checking doc')
// If the document doesn't exist, create it and add to the firestore database
if (!doc.exists) {
console.log('inside customer')
const customer = {
customerCreationTimestamp: firebase.firestore.Timestamp.now(),
username: user.displayName,
email: user.email
}
firebase.firestore().collection("study_guide_customers").doc(user.uid).set(customer)
.then(() => {
// After docuement for user is created, set login status
setIsLoggedIn(!!user)
setIsLoading(false)
})
.catch((error) => {
console.error("Error writing document: ", error);
});
// If document for user exists, set login status
} else {
setIsLoggedIn(!!user)
setIsLoading(false)
}
})
}
} catch {
console.log('Error checking firestore existence and logging in...')
}
})
}, [isLoggedIn, isLoading, setIsLoading, setIsLoggedIn])
const value = {
signOut,
isLoggedIn,
isLoading,
setIsLoading,
setIsLoggedIn,
signInWithRedirect,
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
Original app.js
/* Stripe Security */
import '#stripe/stripe-js'
/* Functionality */
import React from "react"
import { Router } from "#reach/router"
import PrivateRoute from "../components/PrivateRoute"
import Profile from "../components/Profile"
import Login from "../components/Login"
import Projects from "../components/Projects"
import IndexPage from "./index"
import NotFoundPage from './404'
import { AuthProvider } from "../contexts/context"
const App = () => (
<AuthProvider>
<Router basepath="/app">
<PrivateRoute path="/profile" component={Profile} />
<Login path="/login" component={Login}/>
<IndexPage path="/" component={IndexPage}/>
<Projects path="/projects" component={Projects} />
</Router>
</AuthProvider>
)
export default App
Original index.js
/* Stripe Security */
import '#stripe/stripe-js'
/* Functionality */
import * as React from "react"
import IndexContact from "../components/Index/Contact"
import IndexSelectedProjects from "../components/Index/SelectedProjects"
import IndexFeaturedProjects from "../components/Index/FeaturedProjects"
import IndexFooter from "../components/Index/Footer"
import IndexStudyGuide from "../components/Index/StudyGuide"
import IndexNavbar from "../components/Index/Navbar"
import IndexHeader from "../components/Index/Header"
import IndexAbout from '../components/Index/About'
import IndexExperience from '../components/Index/Experience'
import { useMount } from 'react-use';
const IndexPage = () => {
useMount(() => localStorage.setItem('loading', false));
return (
<>
<IndexNavbar />
<IndexHeader />
<IndexAbout />
<IndexExperience />
<IndexFeaturedProjects />
<IndexSelectedProjects />
<IndexStudyGuide />
<IndexContact />
<IndexFooter />
</>
)
}
export default IndexPage
Then in any component I could simply use the following code to access the context
import { useAuth } from "../contexts/context"
const { isLoggedIn, signInWithRedirect, isLoading } = useAuth()
Child components are mounted before parent. Fix your context.js file to add a default value for isLoggedIn state:
const defaultState = {
isLoggedIn: false,
setIsLoggedIn: () => {}
};
const AuthContext = React.createContext(defaultState);
Your defaultState should also include default methods for any parts of the context you wish to work with.
I created a component in which i requested the data from rapid api. After that i got the coin summary in coinRanking after that i am trying to pass data in homePage but i couldn't. Please help me with sharing data using useContext.
import React, { useState } from 'react'
import { useQuery } from "react-query"
import axios from 'axios'
import { createContext } from 'react';
import HomePage from '../Pages/HomePage';
const options = {
method: 'GET',
url: 'https://coinranking1.p.rapidapi.com/coins',
params: {
referenceCurrencyUuid: 'yhjMzLPhuIDl',
timePeriod: '24h',
'tiers[0]': '1',
orderBy: 'marketCap',
orderDirection: 'desc',
limit: '50',
offset: '0'
},
headers: {
'X-RapidAPI-Key': 'ea3e0e9305msh25681129077648ep1f06f9jsnb2ee8da018dc',
'X-RapidAPI-Host': 'coinranking1.p.rapidapi.com'
}
};
const DataContext = createContext({coinRanking}) // i don't know what to pass over here.
function QueryData({children}) {
const getData = axios.request(options)
const {data, isLoading, isError, error, isFetching} = useQuery("getData", ()=>getData,{})
if (isLoading){
console.log("Loading");
return <p>Loading</p>
} else if (isError){
console.log("error");
return <p>{error}</p>
}
const coinRanking= data?.data?.data.stats
return (
<div>
<DataContext.Provider value={{coinRanking}}> //i am trying to pass coinRanking in Homepage
<HomePage/>
</DataContext.Provider>
</div>
)
}
export default DataContext
Well, i spent too much time on finding the answer on my own and watching and exploring lots of video and documentation. Finally, i got an answer where i was but due to minor error i thought this is not doable. However, i was wrong. I tried again and it is working...
import React, { useState } from 'react'
import { useQuery } from "react-query"
import axios from 'axios'
import { createContext } from 'react';
import HomePage from '../Pages/HomePage';
const options = {
method: 'GET',
url: 'https://coinranking1.p.rapidapi.com/coins',
params: {
referenceCurrencyUuid: 'yhjMzLPhuIDl',
timePeriod: '24h',
'tiers[0]': '1',
orderBy: 'marketCap',
orderDirection: 'desc',
limit: '50',
offset: '0'
},
headers: {
'X-RapidAPI-Key': 'ea3e0e9305msh25681129077648ep1f06f9jsnb2ee8da018dc',
'X-RapidAPI-Host': 'coinranking1.p.rapidapi.com'
}
};
export const DataContext = createContext() //i am trying to pass data through this context
function QueryProvider({children}) {
const getData = axios.request(options)
const {data, isLoading, isError, error, isFetching} = useQuery("getData", ()=>getData,{})
if (isLoading){
console.log("Loading");
return <p>Loading</p>
} else if (isError){
console.log("error");
return <p>{error}</p>
}
const coinRanking= data?.data?.data.stats
return (
<div>
{
Object.entries(coinRanking).map(([key, value], i)=>{
console.log("this is value");
return(
<>
<p>Hi, i am data</p>
<p key={i}>{key}--{value}</p>
</>
)
})
}
<DataContext.Provider value={{coinRanking}}>
{children}
</DataContext.Provider>
</div>
)
}
export default QueryProvider
and for Home page
import React from 'react'
import {DataContext} from '../DataQuery/dataQuery'
import { useContext } from 'react'
const HomePage = (props) => {
const {coinRanking} = useContext(DataContext)
console.log(coinRanking, "is in the homepage")
return (
<div>
<div>HomePage</div>
</div>
)
}
export default HomePage
most important is the app.js file where you have to use context provider with router
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { QueryClientProvider, QueryClient } from "react-query"
import './App.css'
import HomePage from './components/Pages/HomePage'
import QueryProvider,{DataContext} from './components/DataQuery/dataQuery'
import { ReactQueryDevtools } from 'react-query/devtools'
const queryClient = new QueryClient()
function App() {
return (
<div>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<QueryProvider>
<Routes>
<Route path="/" element={<HomePage/>}></Route>
</Routes>
</QueryProvider>
</BrowserRouter>
{/* <ReactQueryDevtools initialIsOpen={false} position="bottom-right"/> */}
</QueryClientProvider>
</div>
)
}
export default App
I am not able to render the "profile" component leveraging the following code. When calling the function it simply states that there is not a component to call. I am not seeing anything wrong here. Working on the latest versions of react.
Error Message: Matched leaf route at location "/profile" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
import React, { useState, useEffect } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Nav from './Nav'
import Public from './Public'
import Profile from './Profile'
import Protected from './Protected'
const Router = () => {
const [current, setCurrent] = useState('home')
useEffect(() => {
setRoute()
window.addEventListener('hashchange', setRoute)
return () => window.removeEventListener('hashchange', setRoute)
}, [])
function setRoute() {
const location = window.location.href.split('/')
const pathname = location[location.length-1]
setCurrent(pathname ? pathname : 'home')
}
return (
<BrowserRouter>
<Nav current={current} />
<Routes>
<Route exact path="/" component={Public}/>
<Route exact path="/protected" component={Protected}/>
<Route exact path="/profile" component={Profile}/>
<Route component={Public}/>
</Routes>
</BrowserRouter>
)
}
export default Router
Profile.js
import React, { useState, useEffect } from 'react'
import { Button } from 'antd'
import { Auth, Hub } from 'aws-amplify'
import Container from './Container'
import Form from './Form'
function Profile() {
useEffect(() => {
console.log('made it here--------------->>>>>>>>>>>')
checkUser()
Hub.listen('auth', (data) => {
const { payload } = data
if (payload.event === 'signOut') {
setUser(null)
}
})
}, [])
const [user, setUser] = useState(null)
async function checkUser() {
try {
const data = await Auth.currentUserPoolUser()
const userInfo = { username: data.username, ...data.attributes, }
setUser(userInfo)
} catch (err) {console.log('error: ', err) }
}
function signOut() {
Auth.signOut()
.catch(err=> console.log('error signing out: ', err))
}
if (user) {
return(
<Container>
<h1>Profile</h1>
<h2>Username: {user.username}</h2>
<h3>Email: {user.email}</h3>
<h4>Phone: {user.phone_number}</h4>
<Button onClick={signOut}>Sign Out</Button>
</Container>
);
}
return <Form setUser={setUser} />
}
export default Profile
Thank you for any help you can provide
I want to prevent values in fields of redux-form on unsuccess API response to review user what he has entered wrong but when I submit the form all the values become clear I try destoryonUnmount method in reudForm() but this is not a good approach
import { useDispatch, useSelector } from "react-redux";
import { SubmissionError } from "redux-form";
import Login from "../../components/Login";
function LoginPage() {
const Formdata = async (Formvalues) => {
const responsive = await dispatch(Action.Login(Formvalues));
if (!!responsive.payload.accessToken) {
} else {
throw new SubmissionError({
_error: "Login Failed",
});
}
};
return <Login onSubmit={Formdata} />;
}
export default LoginPage;
here is my form configuration
const validate = (values) => {
const errors = {};
const requiredFields = ["email", "password"];
requiredFields.forEach((field) => {
if (!values[field]) {
errors[field] = "Required";
}
});
return errors;
};
const LoginComponent = reduxForm({
form: "LoginForm",
validate,
forceUnregisterOnUnmount: true,
})(Login);
export default LoginComponent;
I have found the solution by searching it for two to three days because no one answer it
in my routes file, I have to use either React.pureComponent or momo
import React, { Suspense, memo } from "react";
import { Switch, Route } from "react-router-dom";
// Import Containers
import Login from "../container/Login";
const Routes = () => {
return (
<Suspense fallback="Loading......">
<Switch>
<Route exact path="/" component={Authenciated(Login)} />
<Route component={NotFoundPage} />
</Switch>
</Suspense>
);
};
export default memo(Routes);