hy i begin in react and i try to get user data from my api with my cookie, i have the data but when i console.log() the user data i have 4 undefined and then 2 same object data. The problem is i don't know why and when i try to get user.id in an other components using props i have tpyerror id undefined.
import React, {useState, useEffect} from "react";
import NavBar from "./component/NavBar";
import Navigation from "./component/Navigation"
import Cookie from "js-cookie"
import axios from "axios"
import "./App.css";
function App() {
const [user, setUser] = useState()
const [logged, setLogged] = useState(false)
const cookie = Cookie.get("login")
useEffect(() => {
axios.post('/getdatafromcookie', cookie)
.then((res) => {
if(res.data.success === true){
setLogged(true)
setUser(res.data.user)
}
})
}, [])
console.log(user)
return (
<div className="App">
<header className="NavHeader">
<NavBar user={user} logged={logged} />
</header>
<Navigation user={user} logged={logged}/>
</div>
);
}
export default App;
And the console log shows me :
Undefined
Undefined
Undefined
Undefined
{id: "31", email:"test#test.fr" etc.....}
{id: "31", email:"test#test.fr" etc.....}
Navbar
import React, { useState, useEffect } from "react";
import RegisterForm from "../component/RegisterForm";
import RegisterLogin from "../component/RegisterLogin";
import { Navbar, Nav, Dropdown } from "react-bootstrap";
import { Button } from "#material-ui/core"
export default function NavBar(props) {
const [modalLoginShow, setModalLoginShow] = useState();
const [modalRegisterShow, setModalRegisterShow] = useState();
const user = props.user
const islogged = props.logged
if(islogged === false)
{
return (
<Navbar collapseOnSelect expand="lg" bg="transparent" variant="light">
<Navbar.Brand href="/">Matchandate</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"></Nav>
<Nav>
<Nav.Link onClick={() => setModalLoginShow(true)}>
<Button variant="contained" color="secondary">Login</Button>
</Nav.Link>
<Nav.Link onClick={() => setModalRegisterShow(true)}>
<Button variant="contained" color="secondary" >Register</Button>
</Nav.Link>
</Nav>
</Navbar.Collapse>
<RegisterLogin show={modalLoginShow} onHide={() => setModalLoginShow(false)} />
<RegisterForm show={modalRegisterShow} onHide={() => setModalRegisterShow(false)} />
</Navbar>
)
}
return(
<Navbar collapseOnSelect expand="lg" bg="transparent" variant="light">
<Navbar.Brand href="/">Matchandate</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"></Nav>
<Nav>
<Nav.Link href="/profile">
<Button variant="contained" color="primary">Profile</Button>
</Nav.Link>
<Nav.Link href="/logout">
<Button variant="contained" color="primary" >Logout</Button>
</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}
Navigation
import React from "react"
import Slider from "./SliderHome";
import Activate from "./Activate";
import ForgotPwd from "./Pages/ForgotPwd"
import ChangePwd from "./Pages/ChangePwd"
import UserProfile from "./Pages/UserProfil";
import ErrorPage from "./Pages/ErrorPage"
import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom";
export default function Navigation(props){
const user = props.user
const islogged = props.logged
if(islogged){
return(
<Router>
<Switch>
<Route exact path="/" exact component={Slider} />
<Route exact path="/profile" component={() => <UserProfile user={user} />} />
{/* <Route path="/user/:id" component={ChangePwd}/> */}
<Route path="/" component={ErrorPage} />
</Switch>
</Router>
)
}
return (
<Router>
<Switch>
<Route exact path="/" exact component={Slider} />
<Route path="/activate" component={Activate} />
<Route path="/forgot-pwd" component={ForgotPwd}/>
<Route path="/changepwd" component={ChangePwd}/>
{/* <Route path="/user/:id" component={ChangePwd}/> */}
<Route path="/" component={ErrorPage} />
</Switch>
</Router>
)
}
The problem is i don't know why and when i try to get user.id in an other components using props i have tpyerror id undefined.
The reason for that is that you try to get the id of the user before the user is loaded, which means you're doing something like null.id which is undefined (depends on what useState returns when called without any arguments, probably null)
The reason you get multiple Undefined in console.log is:
the first time your App component is rendered, useEffect didn't finish calling the api yet, so user is still undefined, the 2nd time is because you called setLogged(true) so user is still undefined, the 3rd and 4th times ... i'm not sure, but you're probably changing the state somehow which causes a re-render
the proper wait to fix this, would be to wait until user is defined (ie the api call is finished), you can do that by using a simple if statement, something like
if (user.id) {
// return components when the user is logged in
} else {
// return components where the user is not logged in, usually a "loading" screen
}
Now you said user.id returns type error but i couldn't find any user.id in your code, so i assumed that you didn't post the whole thing.
This happens because useEffect is executed after the first render, so, the first time user is null, you will need to guard your code to render once you have data inside user
return user ? <div className=“app”><NavBar user={user}/></div> : <div>loading</div>)
The logs being printed so many times is because strict mode in development
In yours first render: your user is undefined it's normal because your are not defined a default value in your usestate you can change your code by this const [user, setUser] = useState({}) or this
import React, {useState, useEffect} from "react";
import NavBar from "./component/NavBar";
import Navigation from "./component/Navigation"
import Cookie from "js-cookie"
import axios from "axios"
import "./App.css";
function App() {
const [user, setUser] = useState()
const [logged, setLogged] = useState(false)
const cookie = Cookie.get("login")
useEffect(() => {
axios.post('/getdatafromcookie', cookie)
.then((res) => {
if(res.data.success === true){
setLogged(true)
setUser(res.data.user)
}
})
}, [])
console.log(user)
return (
<div className="App">
<header className="NavHeader">
{user ? <NavBar user={user} logged={logged} /> : "loading"}
</header>
{user ? <Navigation user={user} logged={logged}: "loading" />
</div>
);
}
export default App;
Related
I am fetching few products from an API, and displaying them in card. There is a More Details link on the cards, where if the user clicks on it, it will take the user to the selected product details page. My routing to productDetails page works, But I am having troubles to find a way to pass the fetched data to the productDetails page as props.
This is what I have so far:
My FeaturedProduct.js:
import React from "react";
import { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import ProductDetails from "./ProductDetails";
import axios from "axios";
function FeaturedProduct(props) {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts();
}, []);
function fetchProducts() {
axios
.get("https://shoppingapiacme.herokuapp.com/shopping")
.then((res) => {
console.log(res);
setProducts(res.data);
})
.catch((err) => {
console.log(err);
});
}
return (
<div>
<h1> Your Products List is shown below:</h1>
<div className="item-container">
{products.map((product) => (
<div className="card" key={product.id}>
{" "}
<h3>{product.item}</h3>
<p>
{product.city}, {product.state}
</p>
<Router>
<Link to="/productdetails">More Details</Link>
<Switch>
<Route path="/productdetails" component={ProductDetails} />
</Switch>
</Router>
</div>
))}
</div>
</div>
);
}
export default FeaturedProduct;
My Product Details Page:
import React from "react";
import FeaturedProduct from "./FeaturedProduct";
function ProductDetails(props) {
return (
<div>
<div>
<h1>{props.name}</h1>
<h1>{props.color}</h1>
</div>
</div>
);
}
export default ProductDetails;
I am still learning but this is what I would do:
<Route path="/productdetails">
<ProductDetails product={product}/>
</Route>
====
On ProductDetails you can destructure the props:
function ProductDetails(props) {
const {name, color} = props.product;
return (
<div>
<div>
<h1>{name}</h1>
<h1>{color}</h1>
</div>
</div>
);
}
export default ProductDetails;
Pass it as an element with props, if you are using v 6; sorry I didn't ask which version. >
<Switch>
<Route path="/productdetails" element={<ProductDetails {...props} />}/>
</Switch>
if version v4/5 use the render method >
<Route path="/productdetails" render={(props) => (
{ <ProductDetails {...props} />} )}/>
//pass it this way
<Switch>
<Route
path="/productdetails"
render={() => (
{ <ProductDetails product={product}/>})}/>
/>
</Switch>
I want to do a type of authorization in react, but I ran into a problem when I add a token in one component, the 2nd does not see it, but sees it only after reloading the application.
How can this be corrected in the react?
import React from 'react';
import {NavLink} from "react-router-dom";
const Login = () => {
const handleLogin = () => {
console.log( localStorage.getItem('token'));
localStorage.setItem('token', 'token');
}
return (
<div>
<header className="">
<ul>
<li><NavLink to="/main" activeClassName={'active-link'}>Home</NavLink></li>
</ul>
</header>
<button onClick={handleLogin}>login</button>
</div>
);
};
export default Login;
App.js
import './App.css';
import React from 'react';
import {Route,Redirect,Switch} from 'react-router-dom'
import Login from "./pages/login";
import Logout from "./pages/logout";
import Main from "./pages/main";
import ErrorPage from "./pages/error";
function App() {
return (
<div className="App">
<div>ore</div>
<Switch>
<Route exact path="/">
<Redirect to="/login" />
</Route>
{
localStorage.getItem('token') ? <Route path="/main" component={Main} /> : null
}
<Route path="/login" component={Login} />
<Route path="/portfolio" component={Logout} />
<Route path="/error" exact component={ErrorPage} />
<Redirect to={'/error'} />
</Switch>
</div>
);
}
export default App;
you can use the useEffect and useState into your app.js
const [token, setToken] = useState([]);
const tokenFromLogin = localStorage.getItem("token");
useEffect(() => {
setToken(tokenFromLogin);
}, [jwt]);
I am a beginner in ReactJS and React Router and I am having some issues with my nested router.
For my main router in App.js, things work well. So I can visit my landing page (/), login page (/login), and register page (/register). And once I reach this page, if I do a manual refresh on my Chrome Browser (ctrl r), the page refresh and render accordingly.
Below is my App.js
import React from "react";
import { Router, Switch } from "react-router-dom";
import Login from "./components/Login";
import Register from "./components/Register";
import Dashboard from "./components/Dashboard";
import DynamicLayout from './router/DynamicLayout';
import LandingPage from './components/homepage/LandingPage';
import { history } from "./helpers/history";
const App = () => {
return (
<Router history={history}>
<div className="App">
<Switch>
<DynamicLayout
exact
path="/"
component={LandingPage}
layout="LANDING_NAV"
/>
<DynamicLayout
exact
path="/login"
component={Login}
layout="LOGIN_PAGE"
/>
<DynamicLayout
exact
path="/register"
component={Register}
layout="REGISTER_PAGE"
/>
<DynamicLayout
path="/dashboard"
component={Dashboard}
layout="DASHBOARD_PAGE"
/>
</Switch>
</div>
</Router>
);
};
export default App;
Below is my DynamicLayout.js
import React from "react";
import { BrowserRouter as Route, Switch } from "react-router-dom";
import Login from "../components/Login";
import Register from "../components/Register";
const DynamicLayout = (props) => {
const { component: RoutedComponent, layout, ...rest } = props;
const actualRouteComponent = <RoutedComponent {...props} />;
switch (layout) {
case "LANDING_NAV": {
return <div>{actualRouteComponent}</div>;
}
case "LOGIN_PAGE": {
return <div>{actualRouteComponent}</div>;
}
case "REGISTER_PAGE": {
return <div>{actualRouteComponent}</div>;
}
case "DASHBOARD_PAGE": {
return <div>{actualRouteComponent}</div>;
}
default: {
return (
<div>
<h2>Default Nav</h2>
{actualRouteComponent}
</div>
);
}
}
};
export default DynamicLayout;
The issue is with my nested router which is in my Dashboard component. Basically, once a admin user logged in, they will be shown the admin dashboard.
Below is my Dashboard component.
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { history } from "../helpers/history";
import { useHistory } from 'react-router-dom';
import {
BrowserRouter as Router,
Route,
Switch,
} from "react-router-dom";
import { logout } from "../actions/auth";
import AdminSideNavBar from "../components/admin/AdminSideNavBar";
import AdminManageUsers from "./admin/AdminManageUsers";
import AdminPendingApprovalUsers from "../components/admin/AdminPendingApprovalUsers";
import AdminDeactivatedUsers from "./admin/AdminDeactivatedUsers";
import AdminRegisterInternalUsers from "./admin/AdminRegisterInternalUsers";
import AdminLogs from "../components/admin/AdminLogs";
import BrokerSideNavBar from "../components/broker/BrokerSideNavBar";
import ShareholderSideNavBar from "../components/shareholder/ShareholderSideNavBar";
import Login from "../components/Login"
import AdminActivatedUsers from "./admin/AdminActivatedUsers";
const Dashboard = () => {
const [showAdminDashboard, setShowAdminDashboard] = useState(false);
const [showBrokerDashboard, setShowBrokerDashboard] = useState(false);
const [showShareholderDashboard, setShowShareholderDashboard] =
useState(false);
const { user: currentUser } = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
if (currentUser) {
setShowAdminDashboard(currentUser.roles.includes("ROLE_ADMIN"));
setShowBrokerDashboard(currentUser.roles.includes("ROLE_BROKER"));
setShowShareholderDashboard(
currentUser.roles.includes("ROLE_SHAREHOLDER")
);
}
}, [currentUser]);
const logOut = () => {
dispatch(logout());
};
let history = useHistory();
return (
<div>
{showAdminDashboard && (
<Router history= {history}>
<div className="wrapper">
<AdminSideNavBar />
<Switch>
<Route exact path="/dashboard" component={AdminPendingApprovalUsers} />
<Route exact path="/logs" component={AdminLogs} />
<Route exact path="/manageusers" component={AdminManageUsers} />
<Route exact path="/activeusers" component={AdminActivatedUsers} />
<Route exact path="/deactivatedusers" component={AdminDeactivatedUsers} />
<Route exact path="/registerinternalusers" component={AdminRegisterInternalUsers} />
</Switch>
</div>
</Router>
)}
{showBrokerDashboard && <BrokerSideNavBar />}
{showShareholderDashboard && <ShareholderSideNavBar />}
</div>
);
};
export default Dashboard;
With my side nav bar (AdminSideNavbar component), I can navigate to the various pages. Like /logs, /manageusers, /activeusers etc.
Below is my AdminSideNavBar component
import React from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { logout } from "../../actions/auth";
import {
CDBSidebar,
CDBSidebarContent,
CDBSidebarFooter,
CDBSidebarHeader,
CDBSidebarMenu,
CDBSidebarMenuItem,
} from "cdbreact";
import { NavLink } from "react-router-dom";
const AdminSideNavBar = () => {
const { user: currentUser } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const { isLoggedIn } = useSelector((state) => state.auth);
const logOut = () => {
dispatch(logout());
};
return (
<div className="stickysidenav">
<CDBSidebar textColor="#fff" backgroundColor="#333">
<CDBSidebarHeader prefix={<i className="fa fa-bars fa-large"></i>}>
<a
href="/"
className="text-decoration-none"
style={{ color: "inherit" }}
>
TradeDuh
</a>
<p>{currentUser.username}</p>
{/* {isLoggedIn && (
<div className="wrapper">
<p>{currentUser.username}</p>
</div>
)} */}
</CDBSidebarHeader>
<CDBSidebarContent className="sidebar-content">
<CDBSidebarMenu>
<NavLink exact to="/dashboard" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="columns">Dashboard</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/logs" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="table">Logs</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/uploadcompany" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="edit">Update Nasdaq Stocks</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/manageusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="users-cog">Manage Users</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/activeusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="user-check">Active Users</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/deactivatedusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="user-times">De-activated Users</CDBSidebarMenuItem>
</NavLink>
<NavLink to="/registerinternalusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="user-plus">
Add Internal Users
</CDBSidebarMenuItem>
</NavLink>
<NavLink
exact
to="/login"
activeClassName="activeClicked"
onClick={logOut}
>
<CDBSidebarMenuItem icon="sign-out-alt">
Log Out
</CDBSidebarMenuItem>
</NavLink>
</CDBSidebarMenu>
</CDBSidebarContent>
<CDBSidebarFooter style={{ textAlign: "center" }}>
<div
style={{
padding: "20px 5px",
}}
>
TradeDuh (FDM - S21-Java-02)
</div>
</CDBSidebarFooter>
</CDBSidebar>
</div>
);
};
export default AdminSideNavBar;
The issue is, once I reach the various page, if I do a manual refresh (ctrl r), my whole screen will turn white/blank.
So say if I click on /logs, AdminLogs component is rendered, which is all good. BUT... if I now press ctrl r to do a manual fresh, the AdminLogs component don't show anymore. All I see is a blank white screen.
This is totally different from what is happening in my main router where I can do a manual page fresh and the page will render accordingly.
Any idea on how to solve this? What is my issue here?
Thank you for the help!
Please see this sandbox:
https://codesandbox.io/s/use-context-simple-qygdz?file=/src/App.js
*** You have to go to /check1 to start, and when you reach /check2 there shouldn't be a ddd, but it's still there right now (state not updated)
When I've linked one page to another, the usecontext does not pass the state. Not sure why - but I am glad that with help we were able to pinpoint exactly where the problem is.
maybe it helps if you just use one useState hook from which you update your entire context I included the main parts below (here is a link to a working sample). When i try this i see context changes in every component.
import React from "react";
import "./styles.css";
import ChangeContext from "./components/ChangeContext";
import ViewChange from "./components/ViewChange";
const info = {
artists: null,
messages: null,
songs: null,
userid: "ddd",
accesstoken: null,
refreshtoken: null
};
export const InfoContext = React.createContext();
export default function App() {
const [context, setContext] = React.useState(info);
return (
<InfoContext.Provider value={[context, setContext]}>
<div className="App">
<ChangeContext />
<ViewChange />
</div>
</InfoContext.Provider>
);
}
and then in a component
import React from "react";
import { InfoContext } from "../App";
export default function App() {
const [context, setContext] = React.useContext(InfoContext);
return (
<div className="App">
<h1>{context.userid} uid</h1>
<button
onClick={e => {
setContext({ ...context, userid: 123 });
}}
>
click me
</button>
</div>
);
}
in another component check for changes
import React from "react";
import { InfoContext } from "../App";
export default function ChangeContext() {
const [context, setContext] = React.useContext(InfoContext);
return (
<div className="App">
<h1>{context.userid} uid</h1>
<button
onClick={e => {
setContext({ ...context, userid: 123 });
}}
>
click me
</button>
</div>
);
}
maybe try this instead
const [context, setContext] = useState(info);
return (
<BrowserRouter>
<Route exact path="/signup/:id/:access_token" render={() => <InfoContext.Provider value={[context, setContext]}><Signup /> </InfoContext.Provider>} />
<Route exact path="/" render={() => <Login />} />
<Route exact path="/home/:id/:access_token/:refresh_token" render={() => <Homepage ></Homepage>} />
<Route exact path="/artist/:artistid" render={() => <ArtistPage ></ArtistPage>} />
<Route exact path="/map" render={() => <MapLeaflet />} />
</BrowserRouter>
);
I can't comment yet, but is the userId being updated in the context?
What is the value for console.log(userid) inside artisthomepage.js? Maybe it renders with the old value but then it receives the new one and doesn't re-render the component.
I am trying to manage session after successful login while redirecting to some page on form submit.
I would do this usually, in a class component:
componentDidMount() {
if (context.token) {
return <Redirect to="/" />
}
}
But I want to use React hooks, therefore; the following code is not redirecting anywhere:
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Switch, Route, Redirect, Link } from "react-router-dom";
es6
const HomePage = props => (
<div>
<h1>Home</h1>
</div>
);
const AboutUsPage = props => {
useEffect(() => {
redirectTo();
}, []);
return (
<div>
<h1>About us</h1>
</div>
);
};
function redirectTo() {
return <Redirect to="/" />;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/us">About us</Link>
</nav>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/us" component={AboutUsPage} />
</Switch>
</BrowserRouter>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working sandbox: https://codesandbox.io/s/blue-river-6dvyv?fontsize=14
I have read that if the hook useEffect() returns a function it will only work when the component unmounts. But it should redirect when the component is being mounted.
Any suggestions? Thanks in advance.
You could set redirect variable on the state and based on it redirect in render:
const AboutUsPage = props => {
const [redirect, setRedirect] = useState(false);
useEffect(() => {
setRedirect(true); // Probably need to set redirect based on some condition
}, []);
if (redirect) return redirectTo();
return (
<div>
<h1>About us</h1>
</div>
);
};
You could have it so that the component selectively renders the page based on whether or not the page is given a token.
const AboutUsPage = ({token}) => (
token ? (
<Redirect to="/" />
) : (
<div>
<h1>About us</h1>
</div>
)
);
However, if you would still like to use context when implementing this with React Hooks you can read up on how to use context with hooks in this article. It shows you how you can incorporate context into React with only a few lines of code.
import React, {createContext, useContext, useReducer} from 'react';
export const StateContext = createContext();
export const StateProvider = ({reducer, initialState, children}) =>(
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
Done with hooks and context, your AboutUsPage component would resemble something like this.
import { useStateValue } from './state';
const AboutUsPage = () => {
const [{token}, dispatch] = useStateValue();
return token ? (
<Redirect to="/" />
) : (
<div>
<h1>About us</h1>
</div>
);
};
import {Redirect, Switch} from "react-router-dom";
and inside Switch....
<Switch>
<Redirect exact from="/" to="/home" />
</Switch>
This solved my issue.