PrivateRoute.js
import React from 'react';
import { Route, Navigate } from 'react-router';
const PrivateRoute = ({component:Component, ...rest}) => {
return <Route {...rest} component={(props) =>{
const token = window.localStorage.getItem('token');
if(token){
return <Component {...props} />
}else{
return <Navigate to ={'/signin'}/>
}
}} />
}
export default PrivateRoute;
App.js
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./main";
import { BrowserRouter as Router } from "react-router-dom";
createRoot(document.getElementById("app")).render(
<Router>
<App />
</Router>,
);
export default App;
Main.jsx
import React from "react";
import { Routes, Route } from "react-router-dom";
import Test from "./Test";
function App() {
return (
<div>
<Routes>
<Route path='/' element={<Test />} />
</Routes>
</div>
);
}
ERROR
Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
I got some error like this. But I use new update of react router v6. Please help me to solve this problem.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shopping-Cart</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="app.jsx"></script>
</body>
</html>
app.js
import React from "react";
import { render } from "react-dom";
import { Routes, Route, BrowserRouter as Router } from "react-router-dom";
import InsertUser from "./Containers/User/InsertUser";
import ViewUser from "./Containers/User/ViewUser";
import ViewUsers from "./Containers/User/ViewUsers";
render(
<Router>
<Routes>
<Route path="/" element={<InsertUser />} />
<Route path="/viewUsers" element={<ViewUsers />} />
<Route path="/viewUser/:id" element={<ViewUser />} />
</Routes>
</Router>,
document.getElementById("app")
);
insertUser.jsx
import React, { useState } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
function InsertUser() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const insertData = () => {
const user = {
firstName: firstName,
lastName: lastName,
};
axios
.post("http://localhost:3000/api/user/add", user)
//.then((event) => (window.location.href = "/view-inventory"))
.catch((error) => {
console.error("There was an error!", error);
});
};
return (
<div>
<label for="fname">First name:</label>
<input
type="text"
onChange={(e) => {
setFirstName(e.target.value);
}}
id="fname"
name="fname"
/>
<br />
<label for="lname">Last name:</label>
<input
type="text"
onChange={(e) => {
setLastName(e.target.value);
}}
id="lname"
name="lname"
/>
<br />
<button
onClick={() => {
insertData();
}}
value="Submit"
>
insert
</button>
<br></br>
<button
onClick={() => {
window.location.href = "/viewUsers";
}}
>
View All Users
</button>
</div>
);
}
export default InsertUser;
viweUsers.jsx
import React, { useEffect, useState } from "react";
import axios from "axios";
function ViewUsers() {
const [tableData, setTableData] = useState("");
useEffect(() => {
axios
.get("http://localhost:3000/api/user/getUsers")
.then((response) => {
setTableData(response.data);
})
.catch((error) => {
console.error("There was an error!", error);
});
}, []);
const deteleData = (id) => {
axios
.delete("http://localhost:3000/api/user/delete/" + id)
.then(alert("Deleted"));
};
return (
<div>
<table>
<thead>
<tr>
<th>FirstName</th>
<th>LastName</th>
</tr>
</thead>
<tbody>
{tableData.length > 0
? tableData.map((data) => (
<tr
key={data._id}
onClick={() => {
window.location.href = `/viewUser/${data._id}`;
}}
>
<td>{data.firstName}</td>
<td>{data.lastName}</td>
<td>
<button
onClick={() => {
deteleData(data._id);
}}
>
Delete
</button>
</td>
</tr>
))
: null}
</tbody>
</table>
<br></br>
</div>
);
}
export default ViewUsers;
Route components are only valid as children of a Routes or other Route components, and as the error message says, you can't render it directly in an other component alone. In react-router-dom v6 custom Route components are no more, instead the suggested pattern is to use wrapper components to hold the auth logic and conditionally return the content or a Navigate redirect.
Convert PrivateRoute into a PrivateWrapper:
import { Navigate, Outlet } from "react-router-dom";
const PrivateWrapper = () => {
const token = window.localStorage.getItem('token');
return token ? <Outlet /> : <Navigate to='/signin' replace/>;
}
Render the wrapper around Route components you want to protect access to. The Outlet is where the children routes will be rendered
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<PrivateWrapper/>}>
<Route path="/" element={ <Home />}/>
</Route>
<Route path="/signin" element={ <Signin />} />
<Route path="/signup" element={ <Signup />} />
</Routes>
</Router>
</div>
);
}
Related
I am testing a React/Typescript component using Jest and React Testing-Library. I am doing a simple render test and getting a Reference Error: Worker is not defined.
Why and how would I use a worker in this testing context?
Here is my simple test:
import {render, screen} from '#testing-library/react'
import userEvent from '#testing-library/user-event'
import React from 'react'
import {Router} from 'react-router-dom'
import AppRouter from '../router'
test('AppRouter renders all routes and I can navigate to those pages', () => {
render(<AppRouter />)
screen.debug()
})
And here is the AppRouter component:
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
import { useState } from 'react'
import useLocalStorage from './hooks/useLocalStorage'
import * as Constants from './constants'
import Header from './layout/header/header'
import MainPage from './pages/mainPage/mainPage'
import PostPage from './pages/postPage/postPage'
import UserPage from './pages/userPage/userPage'
import LoginPage from './pages/loginPage/loginPage'
import SignupPage from './pages/signupPage/signupPage'
import NewPage from './pages/newPage/newPage'
import FeedbackPage from './pages/feedbackPage/feedbackPage'
import AdminPage from './pages/adminPage/adminPage'
import SettingPage from './pages/settingPage/settingPage'
import { WebContext } from './context/WebContext'
import Favicon from 'react-favicon'
const AppRouter = () => {
const [adminCode, setAdminCode] = useLocalStorage('admin', '')
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [page, setPage] = useState(Constants.Page.Home)
return (
<BrowserRouter>
<div>
<Favicon url={require('../public/favicon.ico')} />
<WebContext.Provider
value={{
isMenuOpen,
setIsMenuOpen,
page,
setPage,
adminCode,
setAdminCode,
}}
>
<Header />
<Switch>
<Route component={MainPage} path="/" exact={true} />
<Route component={PostPage} path="/post/:id" />
<Route component={UserPage} path="/user" />
<Route component={LoginPage} path="/login" />
<Route component={SignupPage} path="/signup" />
<Route component={NewPage} path="/new" />
<Route component={FeedbackPage} path="/feedback" />
<Route component={AdminPage} path="/admin" />
<Route component={SettingPage} path="/setting" />
<Route component={() => <Redirect to="/" />} />
</Switch>
</WebContext.Provider>
</div>
</BrowserRouter>
)
}
export default AppRouter
Most of what I researched on this were old Jest stackoverflows. I am aware there is a jest-worker package but not sure why I would need this or how I would use it when running this simple test.
Here is a link to jest-worker.
Code Trace:
Header component
import { useContext, useState } from 'react'
import { NavLink, useHistory, useLocation } from 'react-router-dom'
import { observer } from 'mobx-react-lite'
import { WebContext } from '../../context/WebContext'
import UnirepContext from '../../context/Unirep'
import UserContext from '../../context/User'
const Header = () => {
const history = useHistory()
const location = useLocation()
const { isMenuOpen, setIsMenuOpen } = useContext(WebContext)
const [searchInput, setSearchInput] = useState<string>('')
const unirepConfig = useContext(UnirepContext)
const userContext = useContext(UserContext)
const gotoNewPage = () => {
if (
userContext.userState &&
userContext.netReputation >= unirepConfig.postReputation
) {
history.push(`/new`, { isConfirmed: true })
}
}
const gotoUserPage = () => {
history.push(`/user`, { isConfirmed: true })
}
const openMenu = () => {
if (!isMenuOpen) {
console.log('open menu!')
setIsMenuOpen(true)
}
}
const handleSearchInput = (event: any) => {
console.log('search input : ' + event.target.value)
}
return (
<header>
<div className="navLinks">
<NavLink to="/" className="link" activeClassName="active" exact>
<img
src={require('../../../public/images/unirep-title.svg')}
/>
</NavLink>
</div>
{/* <div className="search-bar">
<div className="search-icon"><FaSearch /></div>
<form>
<input type="text" name="searchInput" placeholder="Search by keyword, user names or epoch key" onChange={handleSearchInput} />
</form>
</div> */}
{userContext.userState ? (
<div className="navButtons">
<div id="rep" onClick={gotoUserPage}>
<img
src={require('../../../public/images/lighting.svg')}
/>
{userContext.netReputation}
</div>
<div
id="new"
className={
location.pathname === '/new'
? 'navBtn chosen'
: 'navBtn'
}
>
<img
src={require('../../../public/images/newpost.svg')}
onClick={gotoNewPage}
/>
</div>
<div
id="user"
className={
location.pathname === '/user'
? 'navBtn chosen'
: 'navBtn'
}
>
<img
src={require('../../../public/images/user.svg')}
onClick={gotoUserPage}
/>
</div>
<div className="navBtn">
<img
src={require('../../../public/images/menu.svg')}
onClick={openMenu}
/>
</div>
</div>
) : (
<div className="navButtons">
<div
id="login"
className="whiteButton"
onClick={() => history.push('/login')}
>
Sign in
</div>
<div
id="join"
className="blackButton"
onClick={() => history.push('/signup')}
>
Join
</div>
<div id="menu" className="navBtn">
<img
src={require('../../../public/images/menu.svg')}
onClick={openMenu}
/>
</div>
</div>
)}
</header>
)
}
export default observer(Header)
EDIT
The problem may stem from using MobX for state management and not wrapping the component in a Provider but still unsure of how to do this.
I need help!!!
I have multiple content that is to be rendered after login. I have successfully created the login page and it is working fine.
but i can render the components just by typing the path name in the url bar of my browser so my login page doesn't make any sense.
i have {dashboard, History , employee }
i can just render these components by typing /dashboard, /history without logging in
which should not work like that
THis is my Login.jsx
import React, { useEffect, useState } from "react";
import "./Login.css";
import logo from "../../../Icons/logo.png";
import { useNavigate, useParams, NavLink } from "react-router-dom";
import axios from "axios";
function Login({ setusers }) {
const history = useNavigate();
const [user, setUser] = useState({
userName: "",
password: "",
});
const handlechange = (e) => {
const { name, value } = e.target;
setUser({
...user,
[name]: value,
});
};
const login = () => {
axios.post("/login", user).then((res) => {
alert(res.data.message);
setusers(res.data.user);
history.push("/dashboard")
});
};
return (
<>
<div className="tskms_login-container">
<div className="tskms_login-container-left">
<img src={logo} alt="logo" />
</div>
<div className="tskms_login-container-right">
<p>Login</p>
<form>
<input
type="text"
name="userName"
value={user.userName}
onChange={handlechange}
placeholder="Username"
/>
<input
type="password"
name="password"
value={user.password}
onChange={handlechange}
placeholder="Password"
/>
<h5>Forgot Password?</h5>
{/* <NavLink to={"/dashboard"}> */}
</form>
<button className="tskms_login-right-btn" onClick={login}>
Login
</button>
<div></div>
{/* </NavLink> */}
</div>
</div>
</>
);
}
export default Login;
this is my app.js
import "./App.css";
import { Routes, Route, useNavigate, Navigate } from "react-router-dom";
import Dashboard from "./Components/After Login/Dashboard/Dashboard";
import Task from "./Components/After Login/Task/Task";
import Employee from "./Components/After Login/Employee/Employee";
import History from "./Components/After Login/History/History";
import Notification from "./Components/After Login/Notification/Notification";
import Popup from "./Components/After Login/Task/Popup";
import Addemploye from "./Components/After Login/Employee/Addemploye";
import EmployeeCard from "./Components/After Login/Employee/EmployeeCard";
import Login from "./Components/Before Login/Login/Login";
import { useState } from "react";
function App() {
const [user, setusers] = useState({});
return (
<>
<Routes>
<Route
path="/"
element={
user && user._id ? <Navigate to="/dashboard" /> : <Login setusers={setusers} />
}
/>
<Route path="/login" element={<Login setusers={setusers} />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/task" element={<Task />} />
<Route path="/createtask" element={<Popup />} />
<Route path="/employee" element={<Employee />} />
<Route path="/history" element={<History />} />
<Route path="/notification" element={<Notification />} />
<Route path="/employee" element={<Addemploye />} />
<Route exact path="/employee/:id" element={<EmployeeCard />} />
</Routes>
</>
);
}
export default App;
You can add HOC component, which will detect if there is authz token. If there is not, you can display information like: 'You need to be logged in to enter this page'.
For example:
export default withDashboardHocs(Dashboard)
And HOC component would look like:
export default function withDashboardHocs<C>(
WrappedComponent: React.ComponentType<C>,
redirect = '/login'
) {
return (props: C) => {
const router = useRouter();
const [authzResult] = "API WITH AUTHZ TOKEN"();
const { data, fetching, error } = authzResult;
if (error) {
router.push(redirect);
}
if (fetching) {
return null;
}
if (!data) {
return null;
}
const whoAmI = data.AuthWhoAmI;
return (
<UserContext.Provider value={whoAmI}>
<WrappedComponent {...props} />
</UserContext.Provider>
);
};
}
I am trying to implement a small project and got an error/warning like above title.
Here is my Index.jsx:
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { BrowserRouter} from "react-router-dom";
import { App } from "./components/app";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<Suspense fallback={<div>Loading...</div>}>
<BrowserRouter>
<App />
</BrowserRouter>
</Suspense>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
App.jsx:
import React, { useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Container } from "#material-ui/core";
import { ToastContainer } from "react-toastify";
import { Route, Routes } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import Sidebar from "../sidebar/Sidebar";
import TopNav from "../topnav/TopNav";
import { AppRoutes } from "../AppRoutes";
import ThemeAction from "../../store/actions/ThemeAction";
import "./App.scss";
const useStyles = makeStyles({
contentStyle: {
margin: "30px auto",
},
});
export const App = () => {
const themeReducer = useSelector((state) => state.ThemeReducer);
const classes = useStyles();
const dispatch = useDispatch();
useEffect(() => {
const themeClass = localStorage.getItem("themeMode", "theme-mode-light");
const colorClass = localStorage.getItem("colorMode", "theme-mode-light");
dispatch(ThemeAction.setMode(themeClass));
dispatch(ThemeAction.setColor(colorClass));
}, [dispatch]);
return (
<Routes>
<Route
render={(routeProps) => (
<div className={`app ${themeReducer.mode} ${themeReducer.color}`}>
<Sidebar routeProps={routeProps} />
<div className="app__content">
<TopNav />
<div className="app__content-main">
<ToastContainer />
<Container className={classes.contentStyle} maxWidth="sm">
<AppRoutes />
</Container>
</div>
</div>
</div>
)}
/>
</Routes>
);
};
And AppRoutes.jsx:
import React from "react";
import { Route, Routes } from "react-router-dom";
import Customers from "../pages/Customers";
import { Dashboard } from "../pages/Dashboard";
import { UserLogin } from "./User/Login";
import { UserSignup } from "./User/Signup/UserSignup";
export const AppRoutes = () => {
return (
<Routes>
<Route index path="/" element={<Dashboard />} />
<Route path="customers" component={<Customers />} />
<Route path="userLogin" element={<UserLogin />} />
<Route path="userSignup" element={<UserSignup />} />
</Routes>
);
};
And the project is not running) I mean white blank on browser window while there is no error except index.tsx:25 No routes matched location "/" .
In react-router-dom#6 there are no longer any render (or component or children function props). Remove the Routes and Route component in App. You will also update Sidebar to access the location object via hook.
App
return (
<div className={`app ${themeReducer.mode} ${themeReducer.color}`}>
<Sidebar routeProps={routeProps} />
<div className="app__content">
<TopNav />
<div className="app__content-main">
<ToastContainer />
<Container className={classes.contentStyle} maxWidth="sm">
<AppRoutes />
</Container>
</div>
</div>
</div>
);
Sidebar
const Sidebar = () => {
const location = useLocation();
const activeItem = sidebar_items.findIndex(
(item) => item.route === location.pathname
);
return (
<div className="sidebar">
<div className="sidebar__logo">
<img src={logo} alt="company logo" />
</div>
{sidebar_items.map((item, index) => (
<Link to={item.route} key={index}>
<SidebarItem
title={item.display_name}
icon={item.icon}
active={index === activeItem}
/>
</Link>
))}
</div>
);
};
AppRoutes
Remove the index prop from the "/" route. When you specify an index route the path prop is ignored. Also, make sure all the routes are correctly using the element prop.
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="customers" element={<Customers />} />
<Route path="userLogin" element={<UserLogin />} />
<Route path="userSignup" element={<UserSignup />} />
</Routes>
I have a login page which has a forgot password link and it takes the user to forgot password page.
When I click on the forgot password link, it changes the URL but does not load the component.
Code for login page
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
// assets
import Logo from "../../../assets/images/kvh-logo.svg";
import bgImgArray from "../../../assets/images/bg";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
import "./Login.css";
const Login = (props) => {
const [loading, setLoading] = useState(false);
const userid = useFormInput("");
const password = useFormInput("");
const [error, setError] = useState(null);
// for changing backgrounds
const [index, setIndex] = useState(0);
return (
<div className="container-fluid backgroundContainer">
<div className="Login">
<div className="login-form-container">
<div className="logo">
<img src={Logo} className="App-logo" alt="logo" />
</div>
<div className="content">
<Form className="login-form">
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<Input
autoFocus
type="email"
aria-label="Username"
aria-describedby="username"
aria-invalid="false"
placeholder="Username or Email"
{...userid}
/>
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<Input
type="password"
placeholder="Password"
aria-label="password"
aria-describedby="password"
{...password}
/>
</InputGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<Button
className="pull-right"
block="true"
type="submit"
bssize="small"
value={loading ? "Loading..." : "Login"}
onClick={handleLogin}
disabled={loading}
>
Login
</Button>
<br />
</div>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default Login;
In routing code, I have Admin Layout which looks after the dashboard and AuthLayout which looks after the Login page.
I tried searching for the solution but unfortunately couldn't find any solutions. Hence, posting it here.
Router Code
import React from "react";
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
import { createBrowserHistory } from "history";
import AdminLayout from "layouts/Admin/Admin.js";
import AuthLayout from "layouts/Auth/Auth.js";
import ResetPassword from "../components/pages/reset-password/ResetPassword";
const hist = createBrowserHistory();
const AppRouter = () => {
return (
<Router history={hist}>
<Switch>
<Route path="/admin" render={(props) => <AdminLayout {...props} />} />
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
<Redirect from="/" to="/auth" />
</Switch>
</Router>
);
};
export default AppRouter;
Adding Auth Layout Code
import React from "react";
import { Route, Switch, Redirect, Link } from "react-router-dom";
import Login from "../../components/pages/login/Login";
import ResetPassword from "../../components/pages/reset-password/ResetPassword";
import routes from "routes/routes.js";
class Pages extends React.Component {
getRoutes = (routes) => {
return routes.map((prop, key) => {
if (prop.collapse) {
return this.getRoutes(prop.views);
}
if (prop.layout === "/auth") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
} else {
return null;
}
});
};
getActiveRoute = (routes) => {
let activeRoute = "WATCH";
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = this.getActiveRoute(routes[i].views);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else {
if (
window.location.pathname.indexOf(
routes[i].layout + routes[i].path
) !== -1
) {
return routes[i].name;
}
}
}
return activeRoute;
};
componentDidMount() {
document.documentElement.classList.remove("nav-open");
}
render() {
return (
<div className="wrapper wrapper-full-page" ref="fullPages">
<div className="full-page">
<Login {...this.props}></Login>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
<Switch>
{this.getRoutes(routes)}
<Redirect from="*" to="/auth/login" />
</Switch>
</div>
</div>
);
}
}
export default Pages;
look at this code that you wrote:
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
it's never going to /forgotPassword because path always match with first Route.
you should use exact props:
<Route exact path="/" render={(props) => <AuthLayout {...props} />} />
<Route exact path="/forgotPassword" component={ResetPassword} />
If you create your own history then you should use Router instead of BrowserRouter.
import {
Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
Because, BrowserRouter comes with its own history and you provide another one, this causes the problem.
Recently I faced this issue with React 18.
Version of react was 18.0.1 and react-router-dom was 5.1.0 so StrictMode is causing the issue
Just remove or comment the Tag <React.StrictMode> and it should work fine.
Wrap inside Router as given below.
import {
BrowserRouter as Router,
} from "react-router-dom";
<Router><Link to="/forgotPassword">Forgot password?</Link> <Router/>
I am trying to navigate from a List of annotations to an concrete Annotation using a Link from React Router. But It didn't render the component.
So, I have an AnnotationList in where I have all the annotations. Each annotation have a "". In my Container.js I have declared the Route like this:
<Route path="/annotationList/annView/:id" component={annView} />
And in the same Container I have the component annView:
const annView = ({ match }) => {
const { id } = match.params;
console.log(id);
return(
<AnnotationView id={id} />
)
}
When I click on the Link, the URL change to the correct one, but it didn't render anything. What Am I doing wrong?
I paste the complete code of these two js files if it helps.
Container.js
import React from 'react';
import { Route, Switch, withRouter, Link } from "react-router-dom";
import ProductList from './List/ProductList';
import CustomersList from './List/CustomersList';
import AppointmentsList from './List/AppointmentsList';
import AnnotationList from './List/AnnotationList';
import AddProduct from './Post/AddProduct';
import '../styles/App.css';
import AnnotationView from './Item/AnnotationView';
function Container({ location }) {
return (
<div>
<Switch location={location}>
<Route path="/productsList" component={ProductList} />
<Route path="/customersList" component={CustomersList} />
<Route path="/appointmentsList" component={AppointmentsList} />
<Route path="/annotationList" component={AnnotationList} />
<Route path="/annotationList/annView/:id" component={annView} />
<Route path="/addProduct" component={AddProduct} />
</Switch>
</div>
);
}
const annView = ({ match }) => {
const { id } = match.params;
console.log(id);
return(
<AnnotationView id={id} />
)
}
export default withRouter(Container);
import React, { Component } from 'react';
import { Table } from 'reactstrap';
import { Link } from 'react-router-dom';
import AnnotationView from '../Item/AnnotationView';
class AnnotationList extends Component {
constructor(props) {
super(props);
this.state = {
annotations: [],
isLoading: false
}
}
componentDidMount() {
this.setState({isLoading: true});
fetch('http://localhost:8080/annotations')
.then(response => response.json())
.then(data => this.setState({annotations: data, isLoading: false}));
}
render() {
const { annotations, isLoading } = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Anotaciones</h2>
<div className="form-group">
<input type="date"/>
</div>
<Table>
{annotations.map((ann) =>
<div>
<tr>
<Link to={`/annotationsList/annView/${ann.id}`}>
<th>{ann.name}</th>
</Link>
</tr>
<tr>
<td>{ann.text}</td>
</tr>
</div>
)}
</Table>
</div>
)
}
}
export default AnnotationList;
Thanks in advance.
You have a typo in your Link component.
You're trying to match this route
<Route path="/annotationList/annView/:id" component={annView} />
but your Link has annotations with an s
/annotationsList/annView/anythingHere
You have to change your Link Component to this:
<Link to={`/annotationList/annView/${ann.id}`}>
<th>{ann.name}</th>
</Link>
Note: I would still recommend that every time you have similar routes you use exact on at least 1 of them to let React Router know how to differentiate between the routes.
function Container({ location }) {
return (
<div>
<Switch location={location}>
<Route path="/productsList" component={ProductList} />
<Route path="/customersList" component={CustomersList} />
<Route path="/appointmentsList" component={AppointmentsList} />
<Route exact path="/annotationList" component={AnnotationList} />
<Route path="/annotationList/annView/:id" component={annView} />
<Route path="/addProduct" component={AddProduct} />
</Switch>
</div>
);
}