Here's a Link component that I made:
const Restaurants = () => {
const meatList = require('./Meat-Shops.json');
return meatList.map((restaurant) => {
return (
<Link to={`/menu/${restaurant.store_name}`}>
<div className="restaurant-container">
<div className="image-container">
<img src={restaurant.image} alt={restaurant.alt} />
</div>
<div className="info-container" >
<h4>{restaurant.store_name}</h4>
<h5>Pick-up</h5>
</div>
</div>
</Link>
);
})
}
When pressed, it will take me (or render) this component:
import React, { useState } from 'react';
import { useParams } from 'react-router-dom'
const Menu = () => {
console.log(useParams());
return (
<div>
<p>Hello world</p>
</div>
)
}
export default Menu
What I am trying to do is to access restaurant.store_name (the URL parameter, variable at the end of the template string of the 'to' prop of the Link component) within the Menu component by using useParams(). But when I invoke useParams(), I get the following error:
error_message_invalid_hook. What I am doing wrong? Also, here is the file that contains the Router tags, if needed:
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Menu from './Menu';
import Restaurants from './Restaurants';
import NavBar from "./NavBar"
const Pages = () => {
return (
<Router>
<NavBar />
<Switch>
<Route exact path="/">
<Restaurants />
</Route>
<Route path="/menu/:store_name" children={<Menu />} />
</Switch>
</Router>
)
}
export default Pages;
And here is the error message when I set:
const { menu } = useParams();
within the body of the 'menu' component (error message: error_message_for_useParams).
You can also check out all of my code for the components (the Menu and Restaurants components) here on my GitHub: https://github.com/Toriong/quick_eat_carnivore_search_and_delivery
You're close to the solution here. In this situation useParams() is looking at the :store_name, so you would destructure store_name from the params: const { store_name } = useParams() inside of your functional component.
Related
So I have this app which have the same layout in all routes except in two, Login and Register. I have been trying to have a totally separate layout for Login and Register routes but I'm unable to do so.
The App component have all the routes and I'm checking with conditionals the value of of the components which I want to hide with the help of window.location.pathnam. If the user is in '/' (which is the login route) or '/register', then return empty string. If they are in some other routes, then return those components.
But right now the login/register route doesn't show the components but if I go to an inner route where there were supposed to be header, sidebar, footer components, I don't see them. They disappeared from the routes where they are supposed to be.
What am I doing wrong here?
Here's the App comp:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Header from './components/Header/Header';
import Sidebar from './components/Sidebar/Sidebar';
import Footer from './components/Footer/Footer';
import style from './App.module.css';
import RegisterScreen from './screens/RegisterScreen/RegisterScreen';
import LoginScreen from './screens/LoginScreen/LoginScreen';
import Screen3 from './screens/Screen3/Screen3';
import Screen4 from './screens/Screen4/Screen4';
import Screen5 from './screens/Screen5/Screen5';
import Screen6 from './screens/Screen6/Screen6';
const App = () => {
let header = window.location.pathname === '/' || '/register' ? '' : <Header />;
let sidebar = window.location.pathname === '/' || '/register' ? '' : <Sidebar />;
let footer = window.location.pathname === '/' || '/register' ? '' : <Footer />;
return (
<Router>
{/* <Header /> */}
{header}
<div className={style.appBody}>
{/* <Sidebar /> */}
{sidebar}
<main className={style.main}>
<Switch>
<Route path='/' component={LoginScreen} exact />
<Route path='/register' component={RegisterScreen} exact />
<Route path='/screen3' component={Screen3} exact />
<Route path='/screen4' component={Screen4} exact />
<Route path='/screen5' component={Screen5} exact />
<Route path='/screen6' component={Screen6} exact />
</Switch>
</main>
</div>
{/* <Footer /> */}
{footer}
</Router>
)
}
export default App;
Use useLocation from react-router-dom to get the current pathname.
With the .split() function you're able to get the last item.
In the components that change depending on the location (Header, Footer, ...), create your condition (using ternary operator or switch case condition).
Examples :
Header.js (with ternary operator)
import React from 'react';
import { useLocation } from 'react-router-dom';
export default function Header() {
const path = useLocation().pathname;
const location = path.split('/')[1];
return (
<div>
{location === 'login' ? (
<div>
</div>
) : (
<div>
</div>
)}
</div>
);
}
Footer.js (with switch case condition)
import React from 'react';
import { useLocation } from 'react-router-dom';
import ComponentA from './components/ComponentA';
import ComponentB from './components/ComponentB';
import ComponentC from './components/ComponentC';
export default function Footer() {
const path = useLocation().pathname;
const location = path.split('/')[1];
const checkLocation = (uri) => {
switch (uri) {
case 'login':
return <ComponentA />;
case 'register':
return <ComponentB/>;
case 'about':
return <ComponentC />;
default:
return null;
}
};
return (
<div>
{checkLocation(location)}
</div>
);
}
Demo: Stackblitz
Create two different layout , my suggestion is create src/layouts folder and save here your layouts. On your app.js or index.js use react router two switch between different layouts
Ps. Basically your sidebar, footer, navbar should be inside of your customize layout not on app.js or index.js
Using a simple link to / route to via React browser Router.
I got it to work fine with routing to the root component (/), however it does not function as expected when trying to route to (/drink/:drinkId), though the URL changes and the page loads if I manually try to access it.
App component:
import React from "react";
import "./App.css";
import Cocktails from "./Cocktails";
import { Container } from "react-bootstrap";
import NavApp from "./Navbar";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import DrinkDetails from "./DrinkDetails";
import "./App.css";
function App() {
return (
<Router>
<Container className="App-container">
<NavApp />
<Switch>
<Route exact path="/">
<Cocktails size={20} />
</Route>
<Route exact path="/drink/:drinkId">
<DrinkDetails />
</Route>
</Switch>
</Container>
</Router>
);
}
export default App;
Drink details component:
import { React, useState, useEffect } from "react";
import { Jumbotron, Button } from "react-bootstrap";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams,
} from "react-router-dom";
import axios from "axios";
function DrinkDetails() {
let { drinkId } = useParams();
const [drink, setDrink] = useState(null);
const [ingrdts, setIngrdts] = useState([]);
useEffect(() => {
const getDrinkSpecs = async () => {
const res = await axios.get(
`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${drinkId}`
);
let newDrink = res.data.drinks[0];
setDrink(newDrink);
let newIngrdts = [];
for (let i = 1; i <= 15; i++) {
if (newDrink[`strIngredient${i}`] != null) {
let ingrdtVal = {};
ingrdtVal["ing"] = newDrink[`strIngredient${i}`];
ingrdtVal["val"] = newDrink[`strMeasure${i}`];
newIngrdts.push(ingrdtVal);
}
}
setIngrdts([...newIngrdts]);
};
getDrinkSpecs();
}, [drinkId]);
return drink ? (
<Jumbotron>
<h1>
{drink.strDrink}
<img src={drink.strDrinkThumb} />
</h1>
<p>Glass: {drink.strGlass}</p>
<p>Category: {drink.strCategory}</p>
<p>Instructions: {drink.strInstructions}</p>
{ingrdts.map((ingrdt) => (
<p>
{ingrdt.ing} : {ingrdt.val}
</p>
))}
<p>
<Button variant="primary">Learn more</Button>
</p>
</Jumbotron>
) : (
<Jumbotron>
<h1>Hmmm... we don't have this yet!</h1>
<p>
This is a simple hero unit, a simple jumbotron-style component for
calling extra attention to featured content or information.
</p>
<p>
<Button variant="primary">Learn more</Button>
</p>
</Jumbotron>
);
}
export default DrinkDetails;
and this is where I use Link:
import React from "react";
import { Button, Card } from "react-bootstrap";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import "./Card.css";
function CardDrink({ data, select }) {
return (
<Router>
<Card className="Cocktail-card">
<Link to={`/drink/${data.idDrink}`}>
<Card.Img
variant="top"
src={data.strDrinkThumb}
className="Cocktail-card-img"
onClick={() => select(data.idDrink)}
/>
</Link>
<Card.Body>
<Card.Title>{data.strDrink}</Card.Title>
</Card.Body>
</Card>
</Router>
);
}
export default CardDrink;
Remove <Router> from CardDrink component. You need only one Router at root level which you already have in App component.
Also, as a practice don't keep unused imports in your component. I see Router imported in DrinkDetails component as well.
From docs
To use a router, just make sure it is rendered at the root of your element hierarchy. Typically you’ll wrap your top-level element in a router.
Try to use the useHistory() hook:
import React from "react";
import { Button, Card } from "react-bootstrap";
import { BrowserRouter as Router, Switch, Route, Link, useHistory } from "react-router-dom";
import "./Card.css";
function CardDrink({ data, select }) {
const history = useHistory()
return (
<Router>
<Card className="Cocktail-card">
<div onClick={()=>history.push(`/drink/${data.idDrink}`)}>
<Card.Img
variant="top"
src={data.strDrinkThumb}
className="Cocktail-card-img"
onClick={() => select(data.idDrink)}
/>
</div>
<Card.Body>
<Card.Title>{data.strDrink}</Card.Title>
</Card.Body>
</Card>
</Router>
);
}
export default CardDrink;
I am a newbie to React. As a practice, I created a small app. In this process, while navigating from component to another I'm passing a parameter. But when I am trying to access this param value in the routed component getting undefined as the value.
Why my code is not working with useParams hook and why it's working with useRouteMatch hook?.
Is there anything I missed w.r.t useParams. Please give me suggestions on how to get the param value with useParam hook instead of useRouteMatch.And what's the difference between both?
Body.js
if (this.state.load) {
this.totalPages = this.state.getVal.total_pages;
this.result = this.state.getVal.results.map((item, index) => {
return (
<MovieCard
posterPath={item.poster_path}
movieTitle={item.title}
originalTitle={item.original_title}
movieId={item.id}
key={item.id}
/>
);
});
} else {
this.result = <LinearProgress color="secondary" />;
}
MovieCard.js
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import MovieProfile from './movie-profile/MovieProfile';
const MovieCard = (props) => {
return (
<div className="column">
<div className="card">
<h3>
<img src={props.posterPath} alt="Avatar" height="200" width="200" />
</h3>
<p>Moview Name: {props.movieTitle} </p>
<p>
Original Title :<Link to={`/movie-profile/${props.movieId}`}>{props.originalTitle}</Link>
<Router>
<Switch>
<Route exact path="/movie-profile/:movieId" component={MovieProfile} />
</Switch>
</Router>
</p>
</div>
</div>
);
};
export default MovieCard;
MovieProfile.js
import React from 'react';
import { useRouteMatch, useParams } from 'react-router-dom';
const MovieProfile = () => {
let { movieId } = useParams();
console.log(movieId);
let match = useRouteMatch({
path: '/movie-profile/:movieId',
strict: true,
sensitive: true,
});
console.log(match);
// const {movieId} = useParams();
// console.log({movieId})
return (
<>
<div>Now showing post {movieId}</div>;<h1>{match.params.movieId}</h1>
</>
);
};
export default MovieProfile;
I am new to React, and I am having an issue redirecting to a new page using JavaScript event handlers. I don't know what I am not getting right.
App.js
import React, { Component } from 'react';
import {Switch, Route} from 'react-router-dom';
import Nav, {Jumbotron} from '../partials/headobjs';
import Footer from '../partials/footer';
import Users from '../components/user';
import UserObj from '../components/userobj';
import Err404 from '../components/err404';
//import css files..
import '../css/styles.css';
class App extends Component {
render() {
return (
<div>
<Nav />
<Jumbotron/>
<Switch>
<Route exact path='/' component={Users}/>
<Route path='/user' component={UserObj}/>
<Route component={Err404} />
</Switch>
<div className='clr'></div>
<Footer />
</div>
);
}
}
export default App;
Users.js
import React, { Component } from 'react';
import {Redirect} from 'react-router-dom';
class Users extends Component {
constructor(){
super();
this.state = {
name:aminu
};
}
gotoURL(usr){
let url = `/user/${usr}`;
//console.log(url);
return <Redirect to={url}/>
}
render() {
return (
<div className="container">
<button onClick={this.gotoURL.bind(this, this.state.name)}>
Go to {this.state.name} page
</button>
</div>
);
}
}
export default Users;
I want the function gotoURL to redirect to a new route, but I'm not sure what I am doing wrong, I have tried using the browserHistory, and now I'm trying out the <Redirect/> class but yet to no avail.
Redirect component needs to be rendered and not returned to the onClick handler which is why it is not working. You can make use of history.push instead
gotoURL(usr){
let url = `/user/${usr}`;
//console.log(url);
this.props.history.push(url);
}
Instead of redirect from function, use render method and state to workout with redirect problem
class Users extends Component {
constructor(){
super();
this.state = {
name:'aminu',
toUser: false
};
}
gotoURL(usr){
let url = `/user/${usr}`;
//console.log(url);
this.setState({toUser:true, goto: url})
}
render() {
if (this.state.toUser === true) {
return <Redirect to={this.state.goto}/>
}
return (
<div className="container">
<button onClick={this.gotoURL.bind(this, this.state.name)}>
Go to {this.state.name} page
</button>
</div>
);
}
}
You can use Link component from react-v4 to change your route.
import { Link } from 'react-router-dom';
<div className="container">
<Link to={`/user/${usr}`}
<button>
Go to {this.state.name} page
</button>
</Link>
</div>
I am new to React and React Router. I have been trying to create a single page application that has a layout with navigation bars and content and a login page that does not load inside this layout. I have been searching the web and stackoverflow, but have not been able to figure this out. To create my app, I used "yarn create react-app appName" and use "yarn add" to add react router. How do I need to create my routes such that I have a login page with its own layout and a main layout where my content can be displayed.
Here is my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
Here is my App.js:
import React, { Component } from 'react';
import Main from "./Main";
import Login, {fakeAuth} from "./components/Login";
import {
Route,
NavLink,
HashRouter,
Redirect,
withRouter,
Switch,
BrowserRouter
} from "react-router-dom";
class App extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Main} />
<Route path="/login" component={Login} />
</Switch>
</BrowserRouter>
);
}
}
export default App;
Here is my Login.js:
import React from 'react';
import {
Route,
NavLink,
HashRouter,
Redirect
} from "react-router-dom";
class Login extends React.Component {
constructor() {
super();
this.state = {
redirectToReferrer: false
}
this.login = this.login.bind(this);
}
login() {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: {
pathname: '/' } }
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return (
<Redirect to={from} />
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
)
}
}
/* A fake authentication function */
export const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100);
}
};
export default Login
Here is my Main.js:
import React, { Component } from 'react';
import Navigation from './components/Navigation'
import LeftSidebar from './components/LeftSidebar';
import RightSidebar from './components/RightSidebar';
import Login, {fakeAuth} from "./components/Login";
import {
Route,
NavLink,
HashRouter,
Redirect,
withRouter,
Switch
} from "react-router-dom";
/* Home component */
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
/* Category component */
const Category = () => (
<div>
<h2>Category</h2>
</div>
)
/* Products component */
const Products = () => (
<div>
<h2>Products</h2>
</div>
)
class Main extends Component {
render() {
return (
<HashRouter>
<div className="container container-fluid">
<Navigation/>
<div className="row">
<div className="col-md-3">
<LeftSidebar/>
</div>
<div className="col-md-6">
<Route exact path="/" component={Home} />
<Route path="/category" component={Category}/>
<PrivateRoute authed={fakeAuth.isAuthenticated} path='/products' component = {Products} />
</div>
<div className="col-md-3">
<RightSidebar/>
</div>
</div>
</div>
</HashRouter>
);
}
}
const PrivateRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/login", state: { from: props.location }
}}
/>
)}
/>
);
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
export default Main;
My links to the routes are in my navigation component:
....
<ul>
<li><Link to="/">Homes</Link></li>
<li><Link to="/category">Category</Link></li>
<li><Link to="/products">Products</Link></li>
</ul>
....
When I click the Home, Category, or Products link, I want the component to load in the SPA layout in Main.js and I want Login.js to load in its own page. Right now, Home, Category, Products, and Login are loading in the main layout. What do I need to change about these routes or other part of my code to get this working?
Edit:
Added my auth code in the Main.js class.
Added Login.js class.
In addition to following some tutorials on this, I have been following the advice from this answer:
Login Page Separated From Single-page Application (SPA) in ReactJS
However, I am not quite getting it. I am not sure exactly what I need to add to make this work.
Here are images illustrating what I am trying to do:
Main.js:
main SPA layout
Login.js:
Login page
Current issue is login is loading in the main layout rather than its own page:
Do NOT want this