Render a new page on react-routing but it renders it inside the current page - reactjs

This displays a card component that the user sees if the user clicks the <Link>read</Link> it should re-render a new page.
import React from "react";
import { Button } from "react-bootstrap";
import "./CardComponent.css";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Content from "./ContentFolder/Content";
function CardComponent(props) {
return (
<Router>
<div class="card">
<div className="uppercard">
<img
className="bookCover"
src={props.img}
alt=""
width="120px"
height="150px"
/>
<h3>{props.title}</h3>
<h6>By{props.author}</h6>
</div>
<div className="lowerCard">{props.points}</div>
<Link to={"/" + props.title + props.author}>Read</Link>
</div>
<Switch>
<Route
exact
path={`/${props.title+props.author}`}
component={Content}
>
<Content title={props.title} author={props.author}
points={props.points}
/>
</Route>
</Switch>
</Router>
);
}
export default CardComponent;
On clicking read I want to render this content component on a different page.
In summary, the goal is to display all the information on a new page when the user clicks on one of the card components.
import React from "react";
import Mynavbar from "../Partials/Mynavbar";
import MyFooter from "../Partials/Footer";
import { Container } from "react-bootstrap";
import "./Content.css";
function Content(props) {
return (
<div>
<Mynavbar />
<Container className="main">
<h4>{props.title}</h4>
<h6>By {props.author}</h6>
<ul>
{props.points.map((point, i) => {
return <li>{point}</li>;
})}
</ul>
</Container>
<MyFooter />
</div>
);
}
export default Content;

Problem: Router inside of CardComponent
The Router needs to exist at the highest level of the App. Everything that is inside of the Router and outside of the Switch will be rendered on every page. So right now your card code will show up even on the Content route. We want the Card and the Content to be separate Routes.
Problem: Ambiguous URL Structure
Do you need for your urls to look like "/${props.title+props.author}"? This is a very bad structure because you cannot possibly work backwards from the URL to the content. What is the content for "/Harry PotterJ.K. Rowling"? Which part is the title and which part is the author? There is no separator so you don't know. You would have to loop through a list of all books, joining their title and author and comparing it to your string.
A typical URL would be based on an id, like "/book/5". I don't see any mention of an id here so we can use the title.
Solution
An app routing might look like this:
function App() {
return (
<Router>
<Switch>
<Route path="/book/:title" component={BookDetails}/>
<Route path="/" component={BookList}/>
</Switch>
</Router>
)
}
Let's get rid of all the routing in CardComponent and make it just show a card for a book with a link to the book details.
function CardComponent(props: Book) {
return (
<div className="card">
<div className="uppercard">
<img
className="bookCover"
src={props.img}
alt=""
width="120px"
height="150px"
/>
<h3>{props.title}</h3>
<h6>By{props.author}</h6>
</div>
<div className="lowerCard">{props.points}</div>
<Link to={"/book/" + props.title}>Read</Link>
</div>
);
}
Our home page might show a list of these cards.
function BookList() {
// get books from somewhere -- a database? a json file?
const books = ???;
return (
<ul className="bookList">
{books.map((book) => (
<CardComponent {...book} key={book.title} />
))}
</ul>
);
}
BookDetails is a separate route, so we need to get the book from the URL.
function BookDetails(props: RouteComponentProps) {
// get the title from the URL
// is automatically encoded and needs to be decoded
const title = decodeURIComponent(props.match.params.title);
// find the book object from your data source
const book = ???
// from a JSON array: BOOKS.find(book => book.title.toLowerCase() === title.toLowerCase() );
// redirect to error page if no matching book
if ( ! book ) {
return <Redirect to="/404" />
}
// can render your Content component, but only after we get the book
return (
<Content {...book} />
)
}

Related

Clicking on a Card component should create a new route and display further information

I'm new to react-router v6
I have 4 components, App, CardList, Card and CardInfo. There is data (an array of objects, each object represents a movie) coming from an API that gets saved in App.js with useState hook.
Within CardList, I use map to iterate over the array to generate a bunch of Card components and passing in data via props.
What I want now is to be able to click on any Card component and for it to navigate to a different route, e.g. localhost:3000/1 (for Card with the id of 1), localhost:3000/2 (for Card with the id of 2) etc. and within each route that corresponds to the Card id, there would be a box/modal (CardInfo.js) component with further information about the movie.
I'm trying to accomplish this with react-router-dom (version 6).
It looks like within the CardList.js or Card.js component you would need to create links (<Link>) and routes (<Route>) (both which are equal to the number of movies in the data) on the fly with the .map function and wrapping the Card component in <Link> and <Route> tags. Something like
{items.map(movie => (
<Route path="/:id" element={<Card items={movies} />} exact>
<Link to={`/${movie.id}`}>
<Card
key={movie.id}
id={movie.id}
name={movie.name}
description={movie.description}
img={movie.image_url}
/>
</Link>
</Route>
))}
Obviously that doesn't work.
App.js:
import './App.css';
import CardList from './features/Card/CardList';
import CardInfo from './features/Card/CardInfo';
import { useState, useEffect } from "react";
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function App() {
const [movies, setMovies] = useState([]);
useEffect(() => {
const getData = fetch('https://api.com/movies')
.then(data => data.json())
.then(items => { setMovies(items) })
}, [])
return (
<BrowserRouter>
<Routes>
<Route path="/:id" element={<CardInfo items={movies} />} exact></Route>
</Routes>
<CardList items={movies} />
</BrowserRouter>
);
}
export default App;
CardList.js:
import React from 'react'
import Card from './Card'
import "./CardList.css";
const CardList = ({ items }) => {
return (
<div className="cardList">
{items.map(movie => (
<Card
key={movie.id}
id={movie.id}
name={movie.name}
description={movie.description}
img={movie.image_url}
/>
))}
</div>
)
}
export default CardList
Card.js:
import React from 'react'
import "./Card.css";
import { Link } from 'react-router-dom';
function Card(props) {
return (
<Link to={`/${props.id}`}>
<div className="card">
<div>
<img src={props.img} />
<h2>{props.name}</h2>
</div>
</div>
</Link>
)
}
export default Card
CardInfo.js:
import React from 'react'
function CardInfo(props) {
return (
<div>
<p>{props.description}</p>
</div>
)
}
export default CardInfo
your code structure is correct. You just need to useLink, you don't need en external Route component for every card you map.
Here is a link for further information. Hope you find it helpful.
https://stackoverflow.com/a/57059249/17715977

Trying to pass an object to anothe page on react

I am trying to make a blog page, where when you select a link from the home page it gives you a detailed page (much like opening a product detail page from a page of products). I am struggling to pass each blog object to the new details page to then display this information. I have tried passing through props and Links but everything I have tried has come back undefined.
App.js - containing routes to the different pages:
<div className="App">
<NavBar/>
<Router>
<Routes>
<Route path="/" exact element={<BlogPosts/>} />
<Route path="/detail/:id" exact element={<BlogDetails/>} />
</Routes>
</Router>
</div>
BlogPosts which is the home page displaying the blogs. It maps through all the blogs to display them.
<div>
<div className="BlogPosts">
{loader === false && (data.map((blog) => (
<div className="BlogContainer" key={blog.id}>
<img className="BlogImage" src={blog.image} alt={blog.title}/>
<div className="BlogTextContainer">
<h1 className="BlogTitle">{blog.title}</h1>
<h2 className="BlogDate">{blog.date}</h2>
<p className="BlogSummary">{blog.summary}</p>
<Link to={{
pathname: `/detail/${blog.id}`,
state: {
blogTitle: blog.title,
blogDate: blog.date,
},
}}
>
View {blog.title}
</Link>
</div>
</div>
)))}
</div>
</div>
The BlogDetails page which is where I would like to display the data
function BlogDetails( ) {
return (
<div>
Display
</div>
)
}
All help is appreciated!!!
You can use the useLocation hook
In BlogPosts component your Link should be:
<Link
to={`/detail/${blog.id}`}
state={{
blogTitle: blog.title,
blogDate: blog.date
}}>
And in BlogDetails component you make use of that useLocation:
import { useLocation } from 'react-router-dom';
const BlogDetails= () => {
const { state } = useLocation(); // Here we you destructuring, so you do not have to display values everywhere doing location.state
console.log(state); // so you can see your state
return (
<>
<p>{state.blogTitle}</p>
<p>{state.blogDate}</p>
</>
);
};
export default BlogDetails;

Route in React Js

I Have two components one is Main.js and Search.js I want to link button in Main.js to navigate to Search.js, but I'm not sure how to do it.
import React from "react";
import classes from "./Main.module.css";
import Aux from "../../hoc/Aux";
import logo from "../../containers/img/Logo.png";
import Search from "../Search/Search";
import { Route } from "react-router-dom";
const Main = () => {
return (
<Aux>
{/* Section starts */}
<section className={classes.Showcase}>
{/* Section Left */}
<div className={classes.ShowcaseLeft}>
<div className={classes.ShowcaseLeftTop}></div>
<div className={classes.ShowcaseLeftBottom}></div>
</div>
{/* Section Right */}
<div className={classes.ShowcaseRight}>
<div className={classes.ShowcaseRightTop}></div>
<div className={classes.ShowcaseRightRest}>
<img src={logo} alt="Logo" />
<p>
Provide weather, water, and climate data, forecasts and warnings
for the protection of life and property and enhancement of the
national economy.
</p>
<Route path="/auth" component={Search} />
</div>
</div>
</section>
</Aux>
);
};
export default Main;
You have two options: either use react-router-dom useHistory hook, or the Link component. Here are the two ways:
// useHistory
import { useHistory } from "react-router-dom";
function Main() {
let history = useHistory();
return (
<button type="button" onClick={()=> history.push("/search")}>
search
</button>
);
}
// Link
import { Link } from "react-router-dom";
function Main() {
let history = useHistory();
return (
<Link to="/search">
<button type="button">
search
</button>
</Link>
);
}
A last piece of advice: I would suggest to add all your paths in a single file, so you can never make any typo. In the end, the ideal would be to write something like: <Link to={path.search}>

How to direct to a new page when a button is clicked using ReactJS?

I am working in React.I have created a button ,which on click should lead the user to the newpage.I made a component About and imported it as well.
I created a function routeChange which would direct to a new page on Clicking the button.But when the button is clicked I am not being directed to any page .
Instead I get an error.
Probably there is not any error with folders.
I imported my About Component as:
import React from 'react';
import {Navbar,NavbarBrand, Jumbotron, Button} from 'reactstrap';
import './App.css';
import Description from './Description';
import './description.css';
import {useHistory,withRouter} from "react-router-dom";
import About from './About';
function App() {
const history=useHistory();
routeChange = () =>{
this.history.push('/About');
}
return (
<withRouter>
<Navbar color="dark">
<div className="container">
<NavbarBrand className="navbar-brand abs" href="/">
Cheat Sheet
</NavbarBrand>
</div>
</Navbar>
<Jumbotron>
<p className="lead">Quick Review ,Revision And Mnemonic Are Always Good</p>
<hr my-2/>
<p className="lead">Page is still under Construction</p>
<Button onClick={routeChange} className="About"color="primary">About Us</Button>
</Jumbotron>
<div className="img-thumbnail">
<Description/>
</div>
<div className="footer">
©Abhilekh Gautam all right reserved.
<p>Follow<a rel="noopener noreferrer"href="https://www.quora.com/profile/Abhilekh-Gautam-1" target="_blank">Abhilekh Gautam</a> On quora</p>
</div>
</withRouter>
)
}
export default App;
a couple issues here.
change function App (){} to const App = () => {} its going to help with your binding later because arrow functions are interpreted differently from declarative functions
this function needs some help
routeChange = () =>{
this.history.push('/About');
}
first of all you have to declare the function as a constant because App is a functional component not a class component.
second of all because App is a functional component you don't need the this keyword because routeChange is an arrow function and is bound to App
your final function should look like this:
const routeChange = () => {
history.push('/About');
}
make your button onClick handler an anonymous function so it is called on click only and not on render
<Button onClick={routeChange}/>
this code makes the route change function get called when the button renders. Instead change it to
<Button onClick={() => routeChange()}
make sure /About is a route to another component in your router or else you will get a 404 error or hit your no match component (if you have one)
your final product should look something like this
in app.js
import React from 'react';
import {Navbar,NavbarBrand, Jumbotron, Button} from 'reactstrap';
import './App.css';
import Description from './Description';
import './description.css';
import {useHistory,withRouter, BrowserRouter, Route, Switch} from "react-router-dom";
import About from './About';
function App() {
return (
<>
<Navbar color="dark">
<div className="container">
<NavbarBrand className="navbar-brand abs" href="/">
Cheat Sheet
</NavbarBrand>
</div>
</Navbar>
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/About' component={About}
</Switch>
</BrowserRouter>
</>
)
}
then your home component would look like this:
import {useHistory} from 'react-router-dom'
const Home = () => {
const history = useHistory();
const routeChange = () => {
history.push('/About');
}
return (
<>
<Jumbotron>
<p className="lead">Quick Review ,Revision And Mnemonic Are Always Good</p>
<hr my-2/>
<p className="lead">Page is still under Construction</p>
<Button onClick={() => routeChange()} className="About"color="primary">About Us</Button>
</Jumbotron>
<div className="img-thumbnail">
<Description/>
</div>
<div className="footer">
©Abhilekh Gautam all right reserved.
<p>Follow<a rel="noopener noreferrer"href="https://www.quora.com/profile/Abhilekh-Gautam-1" target="_blank">Abhilekh Gautam</a> On quora</p>
</div>
</>
)
}
export default Home

can't route successfully between components in react app

I have a static react app (that means there is not server side rendering) located under example.com/web-class.gr. My problem is that I can't route between components when I use my sidebar menu.
For instance. When I navigate to example.com/web-class.gr/css-intermediate the page loads as expected. From now on if I navigate to different lessonName the page is loading as expected. But I also have exercises, which I can't load when I press the corresponding button in my menu. To get an idea this is my index.js file:
import React from 'react';
import { Link as ReactLink } from 'react-router';
import sidebarStore from './Sidebar/SidebarStore';
import lessonValues from '../../lessonValues';
import LessonStore from '../../LessonStore';
import SidebarLink from './Sidebar/SidebarLink';
export default class Sidebar extends React.Component {
constructor() {
super();
this.state = {
SidebarIsCollapse: sidebarStore.getCurrentState()
}
this.NavMdPlaceholderClass = 'hidden-xs col-sm-4 col-md-3 col-lg-3';
}
componentWillMount() {
sidebarStore.on('change', () => {
this.setState({ SidebarIsCollapse: sidebarStore.getCurrentState() });
this.ChangeSidebarState();
});
this.RenderMainMenu();
}
ChangeSidebarState() {
const NAV_DefaultClasses = "col-sm-4 col-md-3 col-lg-3 ";
if (this.state.SidebarIsCollapse) {
this.NavMdPlaceholderClass = NAV_DefaultClasses + "slideInLeft";
} else {
this.NavMdPlaceholderClass = NAV_DefaultClasses + "slideOffLeft";
}
}
RenderMainMenu() {
this.main_menu = [];
for (let link of lessonValues) {
let { Id, url, isExercise, title } = link;
this.main_menu.push(<SidebarLink key={Id} url={url} isExercise={isExercise} title={title}/>);
}
}
render() {
return (
<div class={this.NavMdPlaceholderClass} id="nav-md-placeholder">
<nav id="sidebar">
<ul id="main-menu">
<li class="ripple-btn">
<ReactLink to="/" onClick={this.SetLessonDetails.bind(this)}>
<span class="item-align-fix">
<i class="glyphicon glyphicon-home" style={{'marginRight': '10px'}}></i>
<strong>
<span>AΡΧΙΚΗ</span>
</strong>
</span>
</ReactLink>
</li>
{this.main_menu}
</ul>
</nav>
</div>
);
}
}
here is the SidebarLink component file:
import React from 'react';
import LessonStore from '../../../LessonStore';
import { Link as ReactLink } from 'react-router';
export default class SidebarLink extends React.Component {
SetPageTitle() {
LessonStore.setLesson(this.props.url);
}
render() {
let glyphoconType = 'glyphicon ';
glyphoconType += this.props.isExercise ? 'glyphicon-pencil' : 'glyphicon-ok-sign';
glyphoconType += ' nav-ico untaken-lesson';
return (
<li class="ripple-btn">
<ReactLink to={this.props.url} onClick={() => this.SetPageTitle()} >
<span class="item-align-fix">
<i class={glyphoconType}></i>
<span>{this.props.title}</span>
</span>
</ReactLink>
</li>
);
}
}
But if I refresh the page manually, I am able to reveal the exercise page. But now I can't navigate to any other element. Only if I click it in sidebar menu and manually refresh the page.
To sum up:
The lessons are loading dynamically. I can navigate between them.
I can't navigate to exercises. Only if I click the corresponding exercise and hit the refresh button.
If I'm viewing an exercise (eg exercise-1), I am not able to navigate to any other component.
I use nginx and below is my rule for the project:
location ^~ /web-class.gr/ {
try_files $uri $uri/ =404;
if (!-e $request_filename){
rewrite ^(.*)$ /web-class.gr/index.html break;
}
}
And lastly here is my sidebar component:
import React from 'react';
import { Link as ReactLink } from 'react-router';
import sidebarStore from './Sidebar/SidebarStore';
import lessonValues from '../../lessonValues';
import LessonStore from '../../LessonStore';
import SidebarLink from './Sidebar/SidebarLink';
// some other helper functions here
render() {
return (
<div class={this.NavMdPlaceholderClass} id="nav-md-placeholder">
<nav id="sidebar">
<ul id="main-menu">
<li class="ripple-btn">
<ReactLink to="/web-class.gr/" onClick={this.SetLessonDetails.bind(this)}>
<span class="item-align-fix">
<i class="glyphicon glyphicon-home" style={{'marginRight': '10px'}}></i>
<strong>
<span>AΡΧΙΚΗ</span>
</strong>
</span>
</ReactLink>
</li>
{this.main_menu}
</ul>
</nav>
</div>
);
Is there any problem with ReactLink to? On my apache machine all works as expected. I can't figure out why my program breaks.
Update
I provide the link of the site to help your job become easier. The site is in greek although I believe you can understand it's structure.
web-class.gr
Code on Github
This One worked For me
NB
for React Router
use <BrowserRouter> tag only once to enclose the whole of your application.
Use this tag at the point of Rendering.
For Example
If you are using Create-React-app .Use this at your Index.js file
ReactDom.Render(<BrowserRouter><App/></BrowserRouter>)
Second Update:
Change your render code in react/index.js to
ReactDom.render(
<Router history={browserHistory} >
<Route path="/" component={Layout} >
<IndexRoute component={Index} ></IndexRoute>
<Route path="/exercise-1" name="exercise-1" component={Exercise1} ></Route>
<Route path="/exercise-2" name="exercise-2" component={Exercise2} ></Route>
<Route path="/exercise-3" name="exercise-3" component={Exercise3} ></Route>
<Route path="/exercise-4" name="exercise-4" component={Exercise4} ></Route>
<Route path="/exercise-5" name="exercise-5" component={Exercise5} ></Route>
<Route path="/exercise-6" name="exercise-6" component={Exercise6} ></Route>
<Route path="/:lessonName" name="lesson" component={Lesson} ></Route>
<Route path=":lessonName" name="lesson" component={Lesson} ></Route>
</Route>
</Router>
,app);
i..e the path starts with /

Resources