I am new to React overall and very new to React Router. I am building an app in which a list of reviews is displayed and when a user clicks a review card, they are redirected to a page that displays that card's details. I have the majority of the logic coded, but I am struggling with implementing React router. Here are my components so far.
import React from 'react';
import ReviewCollection from './components/ReviewCollection';
import ReviewCard from './components/ReviewCard';
import { Route, Router, browserHistory } from 'react-router';
class App extends React. Component {
render() {
return (
<Router >
<Router history={browserHistory} />
<ReviewCollection />
<Route path={"/details"} component={ReviewCard} />
</Router>
);
}
}
export default App;
import React from 'react';
import ReviewCard from './ReviewCard';
import { reviews } from '../data';
import {reactLocalStorage} from 'reactjs-localstorage';
import { browserHistory } from 'react-router';
class ReviewCollection extends React.Component {
goToDetails = (review) => {
reactLocalStorage.set('selectedReview', review);
browserHistory.push('/details');
};
render() {
return (
<div className='card-collection'>
{reviews
.filter((review, idx) => idx < 24)
.map((review) => (
<div onClick={() => this.goToDetails(review)}>
<ReviewCard key={review.id} review={review} />
</div>
))}
</div>
)
}
}
export default ReviewCollection;
import React from 'react';
import { reviews } from '../data';
import StarRatings from 'react-star-ratings';
import './Review.css';
const ReviewCard= ({ review }) => {
return (
<div class="card-deck">
{reviews.map((review) => {
return (
<div class="card">
<div key={review.id}>
<h4 class="card-title">{review.place}</h4>
<StarRatings
rating={review.rating}
starRatedColor="gold"
starDimension="20px"
/>
<div class="card-body">{review.content}</div>
<div class="card-footer">{review.author} - {review.published_at}</div>
</div>
</div>
);
})}
</div>
);
};
export default ReviewCard;
I am not confident as to whether or not I am headed in the right direction with how my routes are written overall, and in the browser, I am getting this error: "TypeError: Cannot read property 'getCurrentLocation' of undefined" Can somebody please help me identify the problem? Thanks
Edit #1 Thanks for the advice guys. Here is my App.js component now that I have started to implement react-router-dom instead of react router:
import React from 'react';
import ReviewCollection from './components/ReviewCollection';
import ReviewCard from './components/ReviewCard';
import { BrowserRouter, Router, Route } from 'react-router-dom';
import history from './history';
class App extends React.Component {
render() {
return (
<BrowserRouter >
<Router history={history} />
<ReviewCollection />
<Route path={"/details"} component={ReviewCard} />
</BrowserRouter>
);
}
}
export default App;
But I am still confused as to how I should use browserHistory now in my ReviewCollection.js file. That has not changed and is still using the code browserHistory.push('/details');
It's better to use react-router-dom instead of react-router.Becuase react-router-dom exports dom aware components most interestingly browser.history.
Check this for more info - react-router vs react-router-dom, when to use one or the other?
Since you're using browserHistory.push(), better use withRouter HOC to do a redirection to the relevant page you want.
Check this for more info on that - How to push to History in React Router v4?
Based on OP's suggestions, I believe it's essential to have 2 different paths for home page of the reviews and details page for a particular review (once user clicks). Therefore, using BrowserRouter, it is possible to create routings for both pages. Home page contains ReviewCollection which should render array of ReviewHeader components.
In review page, for a particular review, it should render ReviewDetails but not the ReviewHeader which should be kept separated, otherwise it will create an issue of using same component in 2 different places as you've encountered.
As I told earilier, in order to do a redirection, use withRouter HOC instead of BrowserHistory. Since it's an HOC, you need to wrap the component to use the history object extracted as a prop.
Use the following code-snippets as supprotive material for configuring BrowserRouter and withRouter HOC.
And check this to get more info on react-router: https://reactrouter.com/web/guides/quick-start
App.js
import React from "react";
import ReviewCollection from "./ReviewCollection";
import ReviewCardDetails from "./ReviewCardDetails";
import { BrowserRouter as Router, Route } from "react-router-dom";
class App extends React.Component {
render() {
return (
<Router>
<Route exact path="/" component={ReviewCollection} />
<Route exact path="/details" component={ReviewCardDetails} />
</Router>
);
}
}
export default App;
In App module, I've given exact as a flag to Route component in order to keep our custom component mounting exactly for the given path. Otherwise ReviewCollection component will be kept intact even in /details route.
ReviewCollection
import React from "react";
import ReviewCardHeader from "./ReviewCardHeader";
import { reactLocalStorage } from "reactjs-localstorage";
import { withRouter } from "react-router-dom";
const reviews = [
{
id: 1,
place: "title 1",
rating: 10,
content: "content 1",
author: "author 1",
published_at: "published_at 1",
},
{
id: 2,
place: "title 2",
rating: 12,
content: "content 2",
author: "author 2",
published_at: "published_at 2",
},
];
class ReviewCollection extends React.Component {
goToDetails = (review) => {
reactLocalStorage.set("selectedReview", review);
this.props.history.push({ pathname: "/details", state: { review } });
};
render() {
return (
<div className="card-collection">
{reviews
.filter((review, idx) => idx < 24)
.map((review) => (
<div onClick={() => this.goToDetails(review)}>
<ReviewCardHeader key={review.id} review={review} />
</div>
))}
</div>
);
}
}
export default withRouter(ReviewCollection);
ReviewCardHeader
import React from "react";
import StarRatings from "react-star-ratings";
const ReviewCardHeader = ({ review }) => {
return (
<div key={review.id} className="card-deck">
<div className="card">
<div>
<h4 className="card-title">{review.place}</h4>
<StarRatings
rating={review.rating}
starRatedColor="gold"
starDimension="20px"
/>
<div className="card-body">{review.content}</div>
<div className="card-footer">
{review.author} - {review.published_at}
</div>
</div>
</div>
</div>
);
};
export default ReviewCardHeader;
ReviewCardDetails
import React from "react";
import { useLocation } from "react-router-dom";
import StarRatings from "react-star-ratings";
const ReviewCardDetails = () => {
const location = useLocation();
const { review } = location?.state; // ? - optional chaining
console.log("history location details: ", location);
return (
<div key={review.id} className="card-deck">
<div className="card">
<div>
<h4 className="card-title">{review.place}</h4>
<StarRatings
rating={review.rating}
starRatedColor="gold"
starDimension="20px"
/>
<div className="card-body">{review.content}</div>
<div className="card-footer">
{review.author} - {review.published_at}
</div>
</div>
</div>
</div>
);
};
export default ReviewCardDetails;
Home Page View

Details Page View

Hope this would be helpful to solve your issue. And make sure to have different details for header and details component since header means a summary of a particular review.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With