I have simple react app that lists different shops from a mongodb database for example (HM, Amazon, Lindex etc) and displayed as cards with links (As seen in Boutique.js)
Desired solution:
I want so that each pressed card leads to a new page using router.
For example: If i press the card that is named "HM" then I want to be directed to /boutiques/HM that runs the Template component (as shown in Boutique.js) without parent component being rendered (except for the navbar)
The next card that is named "Amazon" should direct me to /boutiques/Amazon
The next card etc etc
Current solution:
My current solution renders the Template component under the cards whenever you click any of the cards. The cards are still visible in the page. I want so that the Template component renders without the parent showing (the cards). It should be just the navbar and a blank whitepage
Question:
How do I restructure my current solution to reach my desired solution?
App.js
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import React from "react";
import "./CouponDeals.css";
import Boutiques from "./Boutiques";
import Home from './Home';
function App() {
return (
<BrowserRouter>
<div className="main-container">
<div className="navbar-section-container">
<div class="navbar">
<Link to="/">Hem</Link>
<Link to="/boutiques">Butiker</Link>
</div>
</div>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/boutiques/*" element={<Boutiques />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Boutiques.js
import React, { useEffect, useState } from "react";
import {Link, Route, Routes } from "react-router-dom";
import axios from "./Axios";
import Template from "./Template.js";
const Boutiques = () => {
const [deals, setDeals] = useState([]);
useEffect(() => {
async function fetchData() {
const req = await axios.get("/butiker");
setDeals(req.data);
}
fetchData();
}, []);
return (
<React.Fragment>
<div className="header-section">
<h1 className="title">All Boutiques</h1>
</div>
<div className="card-section-container">
<div className="deals-container">
{deals.length > 0 ? (
deals.map((deal, index) => (
<div key={index} className="deal">
<div className="button-container">
<Link to={`${deal.name}`} className="butiker-button">
<img src={deal.imgUrl} alt={deal.name} />
</Link>
</div>
</div>
))
) : (
<p className="no-results">No results found</p>
)}
</div>
</div>
<Routes>
{deals.map((deal, index) => (
<Route
key={index}
path={`${deal.name}`}
element={<Template name={deal.name} />}
/>
))}
</Routes>
</React.Fragment>
);
};
export default Boutiques;
The selected boutique is showing below the cards because you implemented a router in the bottom Boutiques.js. Instead of using this approach you should try to add another dynamic Route in App.js that uses the name of the boutique as a parameter.
App.js
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/boutiques/" element={<Boutiques />} />
<Route path="/boutiques/:name" element={<Template />} />
</Routes>
You will probably have to refactor your Template component so it retrieves the id of the boutique from the URL. You can do this like so:
let { name } = useParams();
Related
I am making a webplatform working with the OpenLibrary API, I am pulling data from the subjects then when you click on one of the books in the subjects, I want to be taken to a page called 'Book' where I display the data.
Now I have set up my Routes (i think they're correct), but when I click one of the items in the list, it doesn't do anything, it changes the URL to the route I set up, but it doesn't take me to the Book component where I display the data. Now, I haven't posted my Book.js file because it just has test code in it to see if I can get to the component. Any help will be appreciated!
Here are some snippets of my code:
App.js:
import React, { useState, useEffect } from "react";
import axios from "axios";
import Works from "./components/Works";
import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
import "./styles.css";
function App() {
const [subjects, setSubjects] = useState([]);
const [worksDetails, setWorkDetails] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const url = "https://openlibrary.org/subjects/animals.json?limit=10";
useEffect(() => {
axios
.get(url)
.then((response) => {
setSubjects(response.data);
setWorkDetails(response.data.works);
setIsLoading(true);
})
.catch((error) => {
console.log(error);
});
}, []);
if (!isLoading) {
return <p>Loading...</p>;
} else {
return (
<>
<Navbar />
<div className="contentHeader">
<h1 className="subjectHeader" style={{ padding: "10px" }}>
Subject: {subjects.name.toUpperCase()}
</h1>
<h1 className="workHeader" style={{ padding: "10px" }}>
Work Count: {subjects.work_count.toLocaleString()}
</h1>
</div>
<div>
<Works works={worksDetails} />
</div>
<Footer />
</>
);
}
}
export default App;
Works.js
import React from "react";
import { Link } from "react-router-dom";
import Book from "../routes/Book";
const Works = (props) => {
return (
<div className="container">
<div>
<div className="heading">
{props.works.map((work) => {
return (
<Link to={`/book/${work.key}`} element={<Book />} key={work.key}>
<ul>{work.title}</ul>
</Link>
);
})}
</div>
</div>
</div>
);
};
export default Works;
Index.js
import { createRoot } from "react-dom/client";
import "./index.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import App from "./App";
import Book from "./routes/Book";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<BrowserRouter>
<Routes>
<Route path="*" element={<App />} />
<Route path="/book" element={<Book />}>
<Route path=":bookId" element={<Book />} />
</Route>
</Routes>
</BrowserRouter>
);
Issue
When you use
<Route path="/book" element={<Book />}>
<Route path=":bookId" element={<Book />} />
</Route>
The Route component is expecting Book to render an Outlet component for the nested Route component. This doesn't appear to be the UX you are going for though. The outer route is matched and rendered, but then because there's no outlet the inner route rendering the actual Book component you want with the param isn't rendered at all.
It turns out that in the fetched data that work.key isn't just an id, but a nested route value it seems, i.e. something like "key": "/works/OL138052W", so the UI is computing a malformed route ("/book//works/OL138052W") and linking to a route that doesn't exist.
Solution
to={`/book${work.key}`} is the path you are linking to, note the removal of the "/" between "book" and the injected work.key value:
<Link key={work.key} to={`/book${work.key}`}>
<ul>{work.title}</ul>
</Link>
Then the should render one of the following:
<Route path="/book/works/:bookId" element={<Book />} />
or
<Route path="/book/works">
<Route path=":bookId" element={<Book />} /> // "/book/works/:bookId"
</Route>
If you don't want to mess with this work.key value then you may want to select one of the other properties, so long as they provide sufficient uniqueness for identification purposes.
I believe your Route should look like this:
<Route path="/book/:bookId" element={<Book />}>
I have created a route in my react project using the methods of react-router-dom version 6.2.1.
But when I tried to fetch one route of a functional component written on the same implementation component it is not working.
App.js
import React from 'react';
import './App.css';
import {Routes ,Route} from 'react-router-dom';
import HomePage from './pages/homepage/homepage.component';
const CarsPage= () => {
<div>
<h1>CARS PAGE</h1>
</div>
}
function App() {
return (
<div>
<Routes >
<Route exact path='/' element={<HomePage />} />
<Route exact path='/cars' element={<CarsPage/>} /> // This route is not working.
</Routes>
</div>
);
}
export default App;
Issue
The hats route is not picking up.
Your forgot the return statement in CarsPage
Change it to:
const CarsPage= () => {
return (
<div>
<h1>CARS PAGE</h1>
</div>
)
}
You need to wrap the components in BrowserRouter.
See everything works here: https://codesandbox.io/s/festive-leavitt-7tr1d?file=/src/App.js
Looking at the above implementation, the CarsPage component does not return anything. Refactor it to return your JSX like so.
const CarsPage = () => (
<div>
<h1>CARS PAGE</h1>
</div>
);
Also, import BrowserRouter from "react-router-dom" and wrap the "react-router-dom" component <Routes /> with BrowserRouter as seen in the docs like so
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
...
function App() {
return (
<div>
<Router>
<Routes>
<Route exact path="/" element={<HomePage />} />
<Route exact path="/cars" element={<CarsPage />} />
</Routes>
</Router>
</div>
);
}
I have some problem with react-router. It doesn't go to Edit page when I click on id. The id is in URL, but it doesn't do anything.
const Main = (props) => {
const { pathname } = props.location;
return (
<Fragment>
<div>
<div className="container">
<Header />
{pathname === "/create" ? <Create /> : null}
{pathname === '/edit/:id' ? <Edit /> : null}
{pathname === "/" ? <Home /> : null}
</div>
</div>
</Fragment>
);
};
export default withRouter(Main);
app.js:
require('./components/Index');
import React from 'react'
import ReactDOM from "react-dom"
import Index from "./components/Index";
import { BrowserRouter as Router} from "react-router-dom";
import { ToastContainer } from 'react-toastify';
const App =() =>{
}
if (document.getElementById('app')) {
ReactDOM.render(<Router> <Index /> <ToastContainer /></Router>, document.getElementById('app'));
}
index.js:
import React from "react";
import { Route, Switch } from "react-router-dom";
import Main from "./CRUD/Main";
import Create from "./CRUD/Create";
import Edit from "./CRUD/Edit";
import Home from "./CRUD/Home";
const Index = (props) => {
return (
<Main>
<Switch>
<Route path="/Create" component={Create} />
<Route path='/edit/:id' component={Edit} />
<Route exact path="/" component={Home} />
</Switch>
</Main>
);
};
export default Index;
I think main.js have some problems with pathname.
You don't need to do conditional rendering like you are doing in Main when using react-router. Switch will automatically render the first child <Route> or <Redirect> that will match the location.
Hence, you need to remove Main from your router component i.e. Index so that it looks like as shown below:
const Index = (props) => {
return (
<>
<NavBar /> {/* NavBar is optional, I just added for example */}
<Switch>
<Route path="/create" component={Create} />
<Route path="/edit/:id" component={Edit} />
<Route exact path="/" component={Home} />
</Switch>
</>
);
}
NavBar: (Links; just for example)
function NavBar() {
return (
<ul>
<li>
<Link to="/create">Go to create</Link>
</li>
<li>
<Link to="/edit/123">Go to edit with id = 123</Link>
</li>
<li>
<Link to="/">Go to home</Link>
</li>
</ul>
);
}
Now, when you click on the above links, it will automatically take you to the related component (as declared in routes i.e. Index). (no manually condition checking)
And example, to retrieve the URL param i.e. id in Edit component using useParams hook:
function Edit() {
const { id } = useParams<{ id: string }>(); // Remove <{ id: string }> if not using TypeScript
return <h2>Edit, {id}</h2>;
}
I've built a react front end for my rest api. I've got them both deployed! However my navbar component has tags and do not work after clicking on them once. This happens in both netlify and gh-pages. Has anyone encountered this solution before? I am using HashRouter instead of ReactRouter. Thanks for taking your time with this :)
import React from "react";
import { Link } from "react-router-dom";
export default ({ context }) => {
const authedUser = context.authenticatedUser;
//Nav bar conditionally rendered on if the user is logged in or not
return (
<div>
<div className="header-div">
<div>
<Link to="/" className="header-div-left">
Student Courses
</Link>
</div>
<nav>
{authedUser ? (
<React.Fragment>
<div className="header-div-right">
<span className="header-div-right">
Welcome {authedUser.firstName} {authedUser.lastName}
</span>
<Link className="header-div-right" to="/signout">
Sign Out
</Link>
</div>
</React.Fragment>
) : (
<React.Fragment>
<div className="header-div-right-up-in">
<Link className="header-div-right" to="/signup">
Sign Up
</Link>
<Link className="header-div-right" to="/signin">
Sign In
</Link>
</div>
</React.Fragment>
)}
</nav>
</div>
</div>
);
};
import React from 'react';
import {
HashRouter as Router,
Route,
Switch
} from 'react-router-dom';
import './App.css';
import CourseDetail from './components/CourseDetail';
import Courses from './components/Courses';
import CreateCourse from './components/CreateCourse';
import Header from './components/Header';
import UpdateCourse from './components/UpdateCourse';
import UserSignIn from './components/UserSignIn';
import UserSignOut from './components/UserSignOut';
import UserSignUp from './components/UserSignUp';
import NotFound from './components/NotFound';
import PrivateRoute from './PrivateRoute';
import withContext from './Context';
//Connecting the SignUp & SignIn to context.
//This shares the data and actions throughout the component tree
/* Adding the const's as the component to the route handler
lets the components UserSignIn & UserSignUp gain access to
the function in context and any data or actions passed into
<Context.Provider value={value}> */
const UserSignInWithContext = withContext(UserSignIn);
const UserSignUpWithContext = withContext(UserSignUp);
const UserSignOutWithContext = withContext(UserSignOut);
/* To let the user know they are signed in
need to make changes to the name display in the end header */
const HeaderWithContext = withContext(Header);
const CoursesWithContext = withContext(Courses);
const CourseDetailsWithContext = withContext(CourseDetail);
const UpdateCourseWithContext = withContext(UpdateCourse)
const CreateCourseWithContext = withContext(CreateCourse)
export default () => (
<Router>
<div >
<HeaderWithContext/>
<Switch>
<Route exact path="/" component={CoursesWithContext} />
<PrivateRoute path="/courses/create" component={CreateCourseWithContext} />
<Route exact path="/courses/:id" component={CourseDetailsWithContext} />
<PrivateRoute path="/courses/:id/update" component={UpdateCourseWithContext} />
<Route path="/signin" component={UserSignInWithContext} />
<Route path="/signup" component={UserSignUpWithContext} />
<Route path="/signout" component={UserSignOutWithContext} />
<Route component={NotFound} />
</Switch >
</div >
</Router>
);
The footer must display a different CTA depending on if the user is on the Home page, or on the Product page. The product page is a single page that renders product information of the item selected on the home pages, and the way I have accomplished that is by using useParams(), and creating the a correlation between the page.id (comes from the data file ) and pageID.
I thought I could do the same thing for the footer, but I am getting an error claiming that everything is undefined, literally, everything.
This is my footer component. As you can see, I am importing the data for the footer from the data folder as "pageItems":
import React from 'react';
import { useParams } from 'react-router';
import pageItems from '../../data/footerData';
function Footer() {
const { pageID } = useParams();
const individualPage = pageItems.find(page => page.id === pageID);
return (
<div className="footer-container">
<p>{individualPage.copy}</p>
<a href="https://www.lego.com/kids" target="_blank">
<img src={individualPage.cta} alt="cta" />
</a>
</div>
);
}
The data file (which is just a javascript file) is structured in the following way:
const footerData = [
{
id: '1',
copy: 'You can find more fun at',
cta: HomeCta,
href: 'https://www.something.com',
},
];
I am sure I could create the correlation between <Route path="/:videoID"> (which works), and the footer, but I can't seem to get it right.
Edit: This is how my routing page is set up at the moment:
return (
<Router>
<Navbar />
<Switch>
<Route path="/:videoID">
<FeaturedVideo />
</Route>
<Route exact path="/">
<Home />
</Route>
</Switch>
<Footer />
</Router>
);
}
Any help will really help. thank you so much in advance!
And as a side note, I am using the react version that uses react hooks
The solution is to keep <Footer /> inside <Router>, but outside of the <Switch> and assign it as a component to a <Route> with as many path values as whereas you want it to display.
Here's a working version on codesandbox
import React from "react";
import "./styles.css";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams
} from "react-router-dom";
const Footer = (props) => {
let { videoID } = useParams();
return <h3>Requested video ID: {videoID}</h3>;
};
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Router>
<Link to="/2">VideoID2 </Link>
<Link to="/3">VideoID3 </Link>
<Link to="/4">VideoID4 </Link>
<Switch>
<Route path="/:videoID">
<p>You are viewing</p>
</Route>
<Route exact path="/"></Route>
</Switch>
<Route exact path={["/", "/:videoID"]} component={Footer} />
</Router>
</div>
);
}