I have a problem with one of my components in which react router returns a blank page when accessing it. It only happens in one component and I do not know why.The component that has the problem is the EditPost component.
App.js
This is where all the routes are.
import "./App.css";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "./components/Home.js";
import Signup from "./components/signup/Signup";
import Login from "./components/Login/Login";
import Dashboard from "./components/Dashboard/Dashboard";
import PrivateRoute from "./components/common/PrivateRoutes";
import { Provider } from "react-redux";
import Store from "./Store";
import { Provider as AlertProvider } from "react-alert";
import AlertTemplate from "react-alert-template-basic";
import { loadUser } from "./actions/auth";
import { useEffect } from "react";
import PostPage from "./components/PostPage/PostPage";
import EditPost from "./components/EditPost/EditPost";
// Alert options
const alertOptions = {
timeout: 3000,
};
function App() {
useEffect(() => {
Store.dispatch(loadUser());
}, []);
return (
<div className="App">
<Provider store={Store}>
<AlertProvider template={AlertTemplate} {...alertOptions}>
<Router>
<Switch>
<Route exact path="/post-edit/:id" component={EditPost} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/login" component={Login} />
<PrivateRoute exact path="/dashboard" component={Dashboard} />
<Route exact path="/" component={Home} />
<Route exact path="/post/:id" component={PostPage} />
</Switch>
</Router>
</AlertProvider>
</Provider>
</div>
);
}
export default App;
EditPost.js
This is the component that does not render
import React, { useState } from "react";
import { Form, Button } from "react-bootstrap";
import { connect } from "react-redux";
import { updatePost } from "../../actions/posts";
const EditPost = (props) => {
console.log(props);
const [title, setTitle] = useState(props.location.postData.title);
const [description, setDescription] = useState(
props.location.postData.description
);
const [body, setBody] = useState(props.location.postData.body);
const titleChange = (e) => {
setTitle(e.target.value);
};
const bodyChange = (e) => {
setBody(e.target.value);
};
const desChange = (e) => {
setDescription(e.target.value);
};
const addClick = (e) => {
e.preventDefault();
const post = { title, body, description };
props.updatePost(post);
};
return (
<div className="mt-4">
<h1>Edit Post</h1>
<Form>
<Form.Group>
<Form.Label>Post Title</Form.Label>
<Form.Control onChange={titleChange} type="text" />
</Form.Group>
<Form.Group>
<Form.Label>Short Description</Form.Label>
<Form.Control onChange={desChange} type="text" />
</Form.Group>
<Form.Group>
<Form.Label>Body</Form.Label>
<Form.Control onChange={bodyChange} as="textarea" rows={3} />
</Form.Group>
<Button onClick={addClick} variant="primary" type="submit">
Edit
</Button>
</Form>
</div>
);
};
const mapDispatchToProps = (dispatch) => {
return {
updatePost: (id, post) => dispatch(updatePost(id, post)),
};
};
export default connect(null, mapDispatchToProps)(EditPost);
Post.js
In this component is the link to the EditPost component.
import React from "react";
import { Card } from "react-bootstrap";
import dateFormat from "dateformat";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
const Post = (props) => {
return (
<Card>
<Card.Body>
<Link to={`/post/${props.id}`}>
<Card.Title>{props.title}</Card.Title>
</Link>
<Card.Text>{props.description}</Card.Text>
</Card.Body>
<Card.Footer>
<div className="d-flex justify-content-between">
<small className="text-muted">
{dateFormat(props.created, "mmmm dS, yyyy")}
</small>
{props.postUser === props.user ? (
<Link to={`/edit-post/${props.id}`}>
<i className="fas fa-edit" style={{ color: "grey" }}></i>
</Link>
) : null}
</div>
</Card.Footer>
</Card>
);
};
const mapStateToProps = (state) => {
if (state.auth.isAuthenticated) {
return {
user: state.auth.user.username,
};
} else {
return {};
}
};
export default connect(mapStateToProps)(Post);
Right now I am just trying to get the component rendered, then I will worry about the props. Thanks in advance.
Your link is /edit-post/${props.id}.
Your matched path is /post-edit/:id.
edit-post and post-edit are not the same thing 😉
I would recommend adding a NoMatch block at the end of your Switch. It'll help you to diagnose these problems faster.
<Route>
<NoMatch />
</Route>
Related
I am trying to pass a parameter to my other component which is productDetail component but when I am trying to access that parameter value using useParam it doesn't seems to work:
App:
import React from "react";
import { Route, Routes } from "react-router-dom";
import MainHeader from "./components/MainHeader.jsx";
import Product from "./pages/Product.jsx";
import ProductDetails from "./pages/ProductDetails.jsx";
import Welcome from "./pages/Welcome.jsx";
const App = () => {
return (
<div style={{ textAlign: "center" }}>
<header>
<MainHeader />
</header>
<Routes>
<Route path="/welcome" element={<Welcome />} />
<Route path="/product" element={<Product />} />
<Route path="/products/:productId" element={<ProductDetails />} />
</Routes>
</div>
);
};
export default App;
ProductDetailes:
import React from "react";
import { useParams } from "react-router-dom";
const ProductDetails = () => {
const params = useParams;
console.log(params.productId);
return (
<div>
<h1>Product Detail</h1>
<p>{params.productId}</p>
</div>
);
};
export default ProductDetails;
The useParams hook is a function and actually needs to be invoked in order to have any effect.
Example:
const ProductDetails = () => {
const { productId } = useParams();
useEffect(() => {
console.log({ productId });
}, [productId]);
return (
<div>
<h1>Product Detail</h1>
<p>{productId}</p>
</div>
);
};
I'm doing fullstack using react and express js including JWT.
The page is not rendering the complete component I imported, it only renders the text but not the functions within it. What I'm trying to do is display the username on the page.
I appreciate any help!
This is my UserProvider.js
import axios from "axios";
import UserContext from "./UserContext";
import React from "react";
import { useEffect, useState } from "react";
export const UserProvider = (props) => {
const baseUrl = "http://localhost:3000/api/users/";
const [ user, setUser ] = useState([]);
useEffect(() => {
async function fetchData() {
await getAllUsers();
}
fetchData();
}, []);
function getAllUsers() {
return axios.get(baseUrl).then(response => setUser(response.data))
};
function createUser(username, password, firstName, lastName) {
let user = { username, password, firstName, lastName };
return axios.post(baseUrl, user)
.then(response => {
return new Promise(resolve => resolve(response.data));
}
);
}
function signInUser(username, password) {
let user = { username, password };
return axios.post(`${baseUrl}/login`, user)
.then(response => {
localStorage.setItem('myPostsToken', response.data.token)
return new Promise(resolve => resolve(response.data));
}
);
}
return (
<UserContext.Provider value={{
user,
createUser,
signInUser
}}>
{ props.children }
</UserContext.Provider>
)
}
This is my UserList.js
import React, { useContext, useState } from 'react';
import '../App.css'
import UserContext from '../contexts/UserContext';
function UsersList(props) {
return (
<UserContext.Consumer>
{
({ user }) => {
return <div>
<h3>Welcome back </h3>
<div>
{user.map((u) => {
console.log(user)
return (
<div>
<div>
<h2>{u.name}</h2>
<p>{"user's name goes here"}</p>
</div>
</div>
)
})}
</div>
</div>
}
}
</UserContext.Consumer>
);
}
export default UsersList;
This is my UserContext.js
import React from "react";
const UserContext = React.createContext();
export default UserContext;
This is my App.js
import React from 'react';
import { Route, BrowserRouter, Routes, Link } from 'react-router-dom'
import SignIn from './components/SignIn'
import SignUp from './components/SignUp'
import PostsList from './components/PostsList'
import NewPosts from './components/NewPosts'
import { PostsProvider } from './contexts/PostsProvider';
import { UserProvider } from './contexts/UserProvider';
import { EditPosts } from './components/EditPosts';
import './App.css'
import NavBar from './components/NavBar';
import UsersList from './components/UsersList'
function App() {
return (
<div className='app'>
<NavBar />
<UserProvider>
<PostsProvider>
<div>
<BrowserRouter>
<Routes>
<Route exact path="/" element={ <SignIn /> } />
<Route path="/signin" element={ <SignIn /> } />
<Route path="/signup" element={ <SignUp /> } />
<Route path="/posts/new" element={ <NewPosts /> } />
<Route path="/postsList" element={ <PostsList /> } />
<Route path="/edit/:id" element={ <EditPosts /> } />
<Route path="/users/" element={ <UsersList /> } />
</Routes>
</BrowserRouter>
</div>
</PostsProvider>
</UserProvider>
</div>
);
}
export default App;
my app.jsx
import axios from "axios";
import { createContext, useState } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import NavBar from "./components/Navbar";
import ProtectedRoute from "./components/ProtectedRoute";
import Home from "./pages/Home";
import Login from "./pages/Login";
import MyBlogs from "./pages/MyBlogs";
import Profile from "./pages/Profile";
import Signup from "./pages/Signup";
export const AuthContext = createContext();
const App = () => {
const [loggedIn, setLoggedIn] = useState(
Boolean(sessionStorage.getItem("loggedIn"))
);
const VUserData = sessionStorage.getItem("userData");
const [userData, setUserData] = useState(
VUserData ? JSON.parse(VUserData) : null
);
function getUserData() {
return axios
.get("/api/user")
.then((res) => {
return Promise.resolve(res.data);
})
.catch((err) => {
return Promise.reject(err.response);
});
}
return (
<BrowserRouter>
<AuthContext.Provider
value={{
loggedIn,
setLoggedIn,
userData,
setUserData,
getUserData,
}}
>
<Routes>
<Route path="/" element={<NavBar />}>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
<Route path="signup" element={<Signup />} />
<Route
path="profile"
element={
<ProtectedRoute>
<Profile />
</ProtectedRoute>
}
/>
<Route
path="my-blogs"
element={
<ProtectedRoute>
<MyBlogs />
</ProtectedRoute>
}
/>
</Route>
</Routes>
</AuthContext.Provider>
</BrowserRouter>
);
};
export default App;
my ProtectedRoute.jsx
import { useContext } from "react";
import { Navigate, useLocation } from "react-router-dom";
import { AuthContext } from "../app";
const ProtectedRoute = ({ children, ...rest }) => {
const { loggedIn, getUserData } = useContext(AuthContext);
const location = useLocation();
if (loggedIn) {
getUserData()
.then((res) => {
return children;
})
.catch((res) => {
return (
<Navigate
to="/login"
replace
state={{ path: location.pathname }}
/>
);
});
} else {
return (
<Navigate to="/login" replace state={{ path: location.pathname }} />
);
}
};
export default ProtectedRoute;
my home.jsx
import React from "react";
import { Outlet } from "react-router-dom";
import NavBar from "../components/Navbar";
const Home = () => {
return (
<React.Fragment>
<NavBar />
<Outlet />
</React.Fragment>
);
};
export default Home;
My problem is after successful login, the profile page not rendering. It's just a blank white page but if I replacegetUserData() and the whole promise resolve with just return children; it's in ProtectedRoute.jsx it's working fine why is that?
how could I check whether the user is logged or not by requesting to the backend in ProtectedRoute.jsx
This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 9 months ago.
Unable to route to admin page even after successful login, displays a blank screen. On successful login system should navigate and display the admin screen. Could someone please advise why the routing is not happening ?
//login.js
import React, { useEffect, useState } from 'react';
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
const Login = () =>{
const { register, errors, handleSubmit } = useForm();
const [loginData, setLoginData] = useState("");
const [helperText, setHelperText] = useState('');
const navigate = useNavigate();
const onSubmit = (data) => {
try {
const userEmail = "dev#test.com"; // for time being just hard code data
const userPassword = "somePass123"; // for time being just hard code data
if(data.email === userEmail && data.password === userPassword ){
localStorage.setItem('loginEmail', userEmail);
setLoginData(userEmail);
navigate('/admin');
window.location.reload(true)
} else {
setHelperText("Invalid login details");
}
} catch (e){
console.log(e);
}
};
console.log(errors);
return (
<div className="wrapper">
<h3>Login</h3>
<section className="col2">
<div className='loginSection'>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Email</label>
<input
type="text"
{...register("email", { required: true})}
/>
<label>Password</label>
<input
type="text"
{...register("password", { required: true})}
/>
<label>
<span className="loginValidationText">{helperText}</span>
</label>
<section className="col4">
<input type="submit" />
</section>
</form>
</div>
</section>
</div>
)
}
export default Login
//protectedRoute.js
import React from "react";
import { Route, BrowserRouter } from "react-router-dom";
export const ProtectedRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) => {
if (localStorage.getItem("loginEmail")) {
return <Component {...props} />;
} else {
return (
<>
<BrowserRouter
to={{
pathname: "/login",
state: {
from: props.location,
},
}}
/>
</>
);
}
}}
/>
);
};
//navigation.js
import React from 'react';
import { NavLink} from 'react-router-dom';
const Navigation = () => {
return (
<div className="App">
<div className="wrapper">
<div id="wrap">
<nav className="siteNavigation_nav_links">
<div className="main_links_nav">
<img className='logoimage' alt="SSS Logo" src="/images/super.png"></img>
<div className="navigationpanel">
<NavLink className="mob_link" to="/">Home</NavLink>
<NavLink className="mob_link" to="/team">Team</NavLink>
<NavLink className="mob_link" to="/login">Login</NavLink>
</div>
</div>
</nav>
</div>
</div>
</div>
)
}
export default Navigation;
//App.js
import React, { useEffect, useState } from 'react';
import { BrowserRouter, Route, Routes, Switch} from "react-router-dom";
import Navigation from './components/navigation';
import Home from "./components/home";
import Team from "./components/team";
import Admin from "./components/admin";
import Login from "./components/login";
import { ProtectedRoute } from "./components/protectedRoute";
function App() {
return (
<BrowserRouter>
<Navigation />
<Routes>
<Route path="/" element={<Home />}>
</Route>
<Route path="/team" element={<Team />}>
</Route>
<Route path="/login" element={<Login />}>
</Route>
<Route path="/admin" element={
<ProtectedRoute >
<Admin />
</ProtectedRoute>
}>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
Change PotectedRoute.js to the code below, as what you are doing is more like what we used to do for React Router Dom v5 while you are using v6.
import { Navigate, useLocation} from "react-router-dom";
export const ProtectedRoute = ({children}) => {
let location = useLocation();
if(!localStorage.getItem("loginEmail")){
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
};
The information from useLocation passed as prop to Navigate can be used in Login so you send the user to that specific url where they were going to instead of a hard coded one (admin in your case), useful if you had multiple protected routes. Though it's not a requirement, you can remove it.
To know more about authentication in React Router Dom v6, visit this example on StackBlitz from their documentation. Don't look at the editor lint errors, it's the most complete and straightforward authentication example.
I have 2 pages both are childrens from App.
_ NavbarComp.js
_ Home.js
Right now I have a functionnal SearchBar in my Home, I'd like to pass the values between NavbarComp & Home.
When I search from my NavbarComp, I'd like to have a render only on the Home page.
This is what it looks like so far.
I now need to remove my input button from Home, and pass the values between both pages Navbar & Home.
I dont think I can use props there, tho.. looking for solutions !
NavbarComp.js
import { Layout } from "antd";
import { Link, useHistory } from "react-router-dom";
import Cookies from "js-cookie";
import { useDispatch } from "react-redux";
import { logout } from "redux/auth/authActions";
import { Nav, Navbar, NavDropdown, Form, FormControl, Button, Row, Col, Container } from "react-bootstrap";
const { Header } = Layout;
export const NavbarComp = ({input, setInput}) => {
const history = useHistory();
const cookie = Cookies.get('token');
const dispatch = useDispatch();
const logMeOut = (e) => {
e.preventDefault();
dispatch(logout());
history.push('/');
}
return (
<>
<Navbar bg="light" expand="lg">
<Form className="d-flex">
<FormControl
type="search"
placeholder="Search"
className="mr-2"
aria-label="Search"
input={input}
/>
</Navar>
</>
)
}
Home.js
import React from 'react';
import { Link } from "react-router-dom";
import { useSelector } from 'react-redux';
import { v4 as uuid_v4 } from "uuid";
export const Home = () => {
const connected = useSelector(state => state.auth.user)
const [input, setInput] = React.useState('');
const [flats, setFlats] = React.useState([]);
const [flatsDefault, setFlatsDefault] = React.useState([]);
React.useEffect(() => {
getListing('real_estates')
}, [])
const getListing = async (url) => {
const config = {
method: 'GET',
};
const response = await fetch(`${process.env.REACT_APP_API_URL}/${url}`, config);
const data = await response.json();
setFlats(data)
setFlatsDefault(data)
};
const updateInput = async (input) => {
const filtered = flatsDefault.filter(flat => {
return flat.title.toLowerCase().includes(input.toLowerCase())
})
setInput(input);
setFlats(filtered);
}
return (
<>
<input
type='text'
input={input}
placeholder={"Search for properties"}
onChange={(e) => updateInput(e.target.value)}
/>
<div className="home-header">
<div className="bg-img-desc">
<h1>List of real estates</h1>
</div>
</div>
<div className="container" style={{ padding: '0 3.5rem' }}>
<ul className="row">
{flats ? (
flats.map(flat => (
<li className="col-12 col-sm-6 col-md-4 col-lg-3 mb-4" key={uuid_v4()}>
<div className="card h-100">
{ flat.images_url && <img src={`${process.env.REACT_APP_API_URL}${flat.images_url[0]}`} className="card-img-top" alt=""/> }
<div className="card-body">
<h5>{flat.title}</h5>
<p>Price : {flat.price} €</p>
<p>location : {flat.location}</p>
{connected && <Link to={`/real_estates/${flat.id}`} className="btn btn-primary">Details</Link>}
</div>
</div>
</li>
))
) : (
<h2>Loading...</h2>
)}
</ul>
</div>
</>)
}
App.js
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import { Layout } from 'antd';
import { NavbarComp } from "components/NavbarComp";
import { Home } from "pages/Home";
import Realestate from "pages/Realestate";
import NewRealestate from "pages/NewRealestate";
import { Other } from "pages/Other";
import { Login } from "pages/Login";
import { Register } from "pages/Register";
import Profile from "pages/Profile";
import { useDispatch, useSelector } from 'react-redux';
import { getUser } from 'redux/auth/authActions';
import Cookies from 'js-cookie';
function App() {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
React.useEffect(() => {
dispatch(getUser(Cookies.get('id')))
console.log(user)
}, [])
return (
<Layout className="layout" style={{ backgroundColor: 'transparent' }}>
<Router>
<NavbarComp />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/real_estates/:id">
<Realestate />
</Route>
<Route path="/new_real_estate">
<NewRealestate />
</Route>
<Route path="/other">
<Other />
</Route>
<Route exact path="/login" >
<Login />
</Route>
<Route exact path="/register" >
<Register />
</Route>
<Route exact path="/profile" >
<Profile user={user} />
</Route>
</Switch>
</Router>
</Layout>
);
}
export default App;
I can see three ways that you can implement to resolve this.
The first one is creating a context, so your state is going to be share through all your app. So when you change it inside your NavbarComp you will be able to get this value in another component.
The second one is similar to context but using redux. I saw that you are using redux in your project, you can share those values through redux.
The other way is to create a useState() inside your app component and pass it as props to your NavbarComp and your home. So this state will be shared between those two.
function App() {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
const [value, setValue] = useState()
React.useEffect(() => {
dispatch(getUser(Cookies.get('id')))
console.log(user)
}, [])
return (
<Layout className="layout" style={{ backgroundColor: 'transparent' }}>
<Router>
<NavbarComp value={value} setValue={setValue}/>
<Switch>
<Route exact path="/">
<Home value={value} setValue={setValue}/>
</Route>
<Route exact path="/real_estates/:id">
<Realestate />
</Route>
<Route path="/new_real_estate">
<NewRealestate />
</Route>
<Route path="/other">
<Other />
</Route>
<Route exact path="/login" >
<Login />
</Route>
<Route exact path="/register" >
<Register />
</Route>
<Route exact path="/profile" >
<Profile user={user} />
</Route>
</Switch>
</Router>
</Layout>
);
}
export default App;