Router not working in React - javascript

I am using BrowserRouter of react through which I want to navigate between pages. Initially there are four images in a page, and after clicking on one image, the image opens up in the screen. There is a back button below that image, which navigates the user back to the first screen with four images.
The code is like this
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import {FirstPage} from './FirstPage.js';
import {BrowserRouter,Route,Router} from 'react-router-dom';
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div>
<BrowserRouter>
<Route path="/" component={FirstPage}><FirstPage/></Route>
</BrowserRouter>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('container'));
FirstPage.js
class FirstPage extends React.Component{
constructor(props){
super(props);
this.state={
list:[],
images:[],
isClicked:false
}
this.loadImages=this.loadImages.bind(this);
this.loadOne=this.loadOne.bind(this);
}
componentDidMount(){
window.addEventListener('load',this.loadImages);
}
loadImages(){
console.log("load");
var that=this;
$.ajax({
type:'GET',
url:'https://demo0813639.mockable.io/getPanos',
datatype:'jsonp',
success:function(result){
var images=that.state.images;
for(var i=0;i<result.length;i++){
that.state.images.push({"pano":result[i].pano,"name":result[i].name});
}
that.setState({
images:images
})
}
})
}
loadOne(pano){
this.setState({
isClicked:true,
imageUrl:pano
})
}
render(){
var list=this.state.list;
if(this.state.isClicked===false){
list=this.state.images.map((result)=>{
//console.log(result.name);
return(<div className="box">
<div className="label">{result.name}</div>
<img src={result.pano} className="image col-md-3" onClick={this.loadOne.bind(this,result.pano)}/>
</div>
)
})
}
else{
list.push(<Panorama imageUrl={this.state.imageUrl}/>)
}
return <div>{list}</div>;
}
}
module.exports={
FirstPage:FirstPage
}
Panorama.js
import 'aframe';
import 'aframe-particle-system-component';
import {Entity, Scene} from 'aframe-react';
import {Link} from 'react-router-dom';
class Panorama extends React.Component{
render(){
return(
<div>
<div className="pano">
<Scene>
<a-assets position="5 5 5">
<img id="myImage" src={this.props.imageUrl} crossorigin="anonymous"/>
</a-assets>
<a-sky src="#myImage"></a-sky>
</Scene>
</div>
<div className="goback"><Link to="/">Go back</Link></div>
</div>
)
}
}
module.exports={
Panorama:Panorama
}
There aren't any errors for the above code, but it doesn't work too. The version of react router is v4. What is wrong above?

It seems like you aren't really taking advantage of React Router here. The reason why the <Link/> doesn't do anything is because you're technically still on the same page as your image box, as you're not moving to another path, therefore the link would not trigger a path change.
I've created a quick example on how you can get past this issue. Of course it's not an exact replica of your project but it gives you an idea of how you can solve this issue.
=== Explanation
Main App
Our Main app consists of two routes. One for selecting an image and the other for viewing an image.
View Image
This displays an image that was sent across by the First Page component. This is one way to do it but you could also store images and active image in App Component and send it down to this child component so that you don't have to keep the image url in the parameter. (OR you could use Redux or some other global state manager).
First Page
We list a series of images via a simple array and keep an active index of the select image. We map these to the view via buttons and trigger an event once they have been clicked on which sets the active image and redirect flag that will redirect the user to the image/:id route with the image url.
Of course, your loading of images can be improved but that's out of scope for this question.
View working version here: https://codesandbox.io/s/pY2LyzAEr
=== Source
import React from 'react';
import { render } from "react-dom";
import { BrowserRouter, Route, Router, Redirect, Switch, Link } from 'react-router-dom';
// *-------------------------
// | First Page
// *-------------------------
class FirstPage extends React.Component{
state = {
currentActive: null,
redirect: false,
images: ['https://cdn.pixabay.com/photo/2014/12/22/10/04/lion-577104__340.jpg', 'http://www.yosemite.com/wp-content/uploads/2013/07/Merced-Lake-back-of-Half-Dome_Kenny-Karst.jpg', 'http://www.japan-guide.com/g9/3060_12.jpg', 'https://cache-graphicslib.viator.com/graphicslib/thumbs674x446/3675/SITours/hong-kong-island-half-day-tour-in-hong-kong-114439.jpg']
}
viewImage = (evt) => {
this.setState({
currentActive: evt.target.dataset.image,
redirect: true
})
}
render(){
return this.state.redirect
? <Redirect to={`/image/${btoa(this.state.images[this.state.currentActive])}`} />
: (
<div>
{this.state.images.map((image, index) => {
return <button key={index} data-image={index} onClick={this.viewImage}>View Image {index + 1}</button>
})}
</div>
)
}
}
// *-------------------------
// | View Image Component
// *-------------------------
const ViewImage = (props) => {
return (
<div>
<img src={`${atob(props.match.params.id)}`} />
<div><Link to="/">Back</Link></div>
</div>
)
}
// *-------------------------
// | Main App.JS
// *-------------------------
class App extends React.Component {
render(){
return(
<div>
<BrowserRouter>
<Switch>
<Route exact path="/" component={FirstPage}/>
<Route path="/image/:id" component={ViewImage}/>
</Switch>
</BrowserRouter>
</div>
)
}
}
render(<App />, document.getElementById("root"));

Related

How do I Link Components correctly using React Router?

I am using React and React Router to try and link my components together for a project. I would like to link off a picture from the Home page (current component) to a different component.
Currently I can click the picture and it kind of acts like a link (turns blue after I click it sometimes), although it doesn't link to the other component, no errors show and nothing changes in the url bar. Nothing literally happens.
Any thoughts? Here is my code:
import { HashRouter as Router, Route,} from 'react-router-dom';
import Header from './Header'
import PortfolioWorkPage from './Pages/PortfolioWorkPage';
class Home extends React.Component {
render () {
return (
<Router>
<Header />
<h1>PORTFOLIO</h1>
<div className="portfolioPic">
<img src='IMAGES/Portfolio-Pics-Logo.jpg' className='portfolioPic'></img>
<Route path='Portfolio' component={PortfolioWorkPage} />
</div>
</Router>
)
}
}
export default Home
Code with error: react-dom.development.js:17117 The above error occurred in the component: in img (created by Home) in div (created by Home) in Router (created by HashRouter) in HashRouter (created by Home) in Home (created by App) in Router (created by HashRouter) in HashRouter (created by App) in App
import { HashRouter as Router, Route, Link} from 'react-router-dom';
import Header from './Header'
import PortfolioWorkPage from './Pages/PortfolioWorkPage';
class Home extends React.Component {
render () {
return (
<Router>
<Header />
<h1>PORTFOLIO</h1>
<div className="portfolioPic">
<img src='IMAGES/Portfolio-Pics-Logo.jpg' className='portfolioPic'>
<Route exact path='Portfolio'><PortfolioWorkPage /></Route>
</img>
</div>
</Router>
)
}
}
export default Home
Following the comments, here are two implementations using internal state and then a route. In the first you stay at the url path mywebsite.com, in the second the url path becomes mywebsite.com/portfolio. In both cases the image will remain on the page - you are not actually being transferred to a new page, you are just selectively rendering componenets based on the path.
Using internal state:
class Home extends React.Component {
constructor(props){
super(props)
this.state = {
portfolioActive: false
}
}
handleClick = () => {
this.setState({portfolioActive: !this.state.portfolioActive})
}
render () {
return (
<>
<h1>PORTFOLIO</h1>
<div className="portfolioPic">
<img src='IMAGES/Portfolio-Pics-Logo.jpg' className='portfolioPic'
onClick={this.handleClick}/>
</div>
{this.state.portfolioActive ? <PortfolioWorkPage/> : null}
</>
)
}
}
export default Home
Using a route:
import { HashRouter as Router, Route, Link} from 'react-router-dom';
class Home extends React.Component {
render () {
return (
<Router>
<h1>PORTFOLIO</h1>
<div className="portfolioPic">
<Link to="/portfolio">
<img src='IMAGES/Portfolio-Pics-Logo.jpg' className='portfolioPic'/>
</Link>
</div>
<Route exact path="/portfolio">
<PortfolioWorkPage />
</Route>
</Router>
)
}
}
export default Home
You can use the Link or NavLink component provided by react router.
import { Link } from 'react-router-dom';
<Link to="/some-url"/>
<Router>
<div>
<Route exact path="/some-url">
<MyComponentWithImage />
</Route>
</div>
</Router>

ReactJS - Redirect in main component

I want to redirect to other page with search results after submitting form (search bar feature).
I will usually redirect with return <Redirect to={'/search/' + this.state.value} /> in render(), but I can't now since I need to render entire site (search bar is in main component).
I already tried this.props.history.push('/search/' + this.state.value) and this.context.router.history.push('/search/' + this.state.value) in place of comment but it doesn't work.
(Code simplified for clarity)
import React, { Component } from "react";
import {
Route,
NavLink,
BrowserRouter
} from "react-router-dom";
import Search from "./Search";
import Autosuggest from 'react-autosuggest';
class App extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.search = this.search.bind(this);
}
// react-autosuggest code
search(e) {
e.preventDefault();
// Redirect to '/search/' + this.state.value
}
render() {
return (
<BrowserRouter>
<div className="app">
<div className="header">
<form className="search-form" onSubmit={this.search}>
{/* Autosuggest input which updates this.state.value */}
</form>
</div>
<div className="main">
<Route path="/search/:handle" component={Search} />
{/* Other Routes */}
</div>
</div>
</BrowserRouter>
);
}
}
export default App;
Both this.props.history and this.context.router.history are undefined if I try to use them. <Redirect /> (obviously) can't be rendered instead of <BrowserRouter />
Have you tried using withRouter HOC to App, giving you access to history as a prop?
class App extends Component {
// ...
search(e) {
e.preventDefault();
this.props.history.push(`/search/${this.state.value}`)
}
// ....
}
export default withRouter(App)
You need to import Redirect from react-router-dom,
import {Redirect} frpm 'react-router-dom'
And usage,
search(e) {
e.preventDefault();
if(this.state.value){
return <Redirect to={`/search/${this.state.value}`}
}
}

Using react api context alongside react router

I have a problem with passing context to route. I get an error when i click a link that goes to my component where context was passed from App component. Below is that component with App (only one import just to show where Context is coming from):
App.js
import { Context } from './Context';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
cryptolist: []
}
}
componentDidMount = () => {
fetch('https://api.coinmarketcap.com/v2/ticker/?structure=array')
.then(response => response.json())
.then(json => this.setState({
cryptolist: json.data
}))
}
render() {
return (
<div>
<Menu />
<Context.Provider value={this.state}>
<Userlist />
</Context.Provider>
</div>
)
}
}
export default App;
Userlist.js ( should be cryptolist or something )
import { Context } from '.././Context'
export default class Userlist extends Component {
render() {
return (
<main>
<Context.Consumer>
{(context) => context.cryptolist.map(el => {
return (
<div>
<h2>{el.name}</h2>
<h5>{el.symbol}</h5>
<h3>{el.quotes.USD.price}</h3>
</div>
)
})}
</Context.Consumer>
</main>
)
}
}
Context.js
import React from 'react';
export const Context = React.createContext();
Everything works just fine here untill i wanted to make a menu that links to this component.
import React from "react";
import { slide as Slider } from 'react-burger-menu';
import { BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import Main from './main';
import Userlist from './userlist';
export default class Menu extends React.Component {
render () {
return (
<Router>
<div className="bg-navy w-100 h-100">
<Slider width={ 180 } isOpen={ false }>
<Link className="menu-item" to="/main">Home</Link>
<Link className="menu-item" to="/crypto">About</Link>
</Slider>
<Switch>
<Route path="/main" component={Main} />
<Route path="/crypto" component={Userlist} />
</Switch>
</div>
</Router>
);
}
}
When i click a link to component Userlist i get an error thats cryptolist is not defined. I get it that Userlist can't see a context after clicking link to it. How to pass it correctly?
You are using the routes in the Menu component. Is this really you want? Though, I don't know how this slide thingy works. Maybe this is the way you want to go. I think your problem occurs because your Menu component is not wrapped by the provider. Try like this:
<Context.Provider value={this.state}>
<Menu />
<Userlist />
</Context.Provider
Your Menu component will call Userlist but as it is out the Provider the context doesn’t exist!
Replace Userlist in Context.Provider by Menu and all will be fine.

React global objects & events

I'm new to React and still having some trouble "thinking in react" when it comes to global events & scope.
My goal is to maintain some global data about the "session" like whether a user is logged-in and their identity.
My main container looks like this:
import React, { Component } from "react";
import { Link, Route, Switch } from 'react-router-dom';
import Header from './Header';
import Routes from "./Routes";
class App extends Component {
render() {
return (
<div className="parent-container">
<Header />
<Content />
</div>
)
}
}
class Content extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="content" >
<Routes />
</div>
)
}
}
export default App;
<Header /> is simply a nav bar with a title & links.
<Routes /> is a react-router which looks like this:
import React from "react";
import { BrowserRouter, Route } from 'react-router-dom';
import HomeView from "../presentational/_Views/HomeView";
import LoginView from "../presentational/_Views/LoginView";
import CreateUserView from "../presentational/_Views/CreateUserView";
import TestView from "../presentational/_Views/TestView";
const Routes = () => (
<div>
<Route exact path="/" component={HomeView} />
<Route path="/login" component={LoginView} />
<Route path="/test" component={TestView} />
<Route path="/create" component={CreateUserView} />
</div>
)
export default Routes;
Each route loads a "presentational" view component that contains child components for that particular view (Login view component imports a LoginForm component etc).
Whenever my app is hard-loaded (or reloaded) I would like the following logic to be fired (pseudo-code):
if session_token exists:
- make a call to API
- verify session
- store a global session object containing session data
I figured I can do something like this in a componentWillMount() function on the App component...
//...
class App extends Component {
componentWillMount(){
if(token && valid(token)){
// store the user session in an object containing token, user-role, etc.
}
}
render() {
//....
..but this seems like a bad design.
The other part to this is to:
show/hide navbar links based on whether a user is logged in (show "login" link if not logged in, show "log out" link is logged in)
check authentication on state change before the view is rendered.
The above two is where I wanna go for context but focusing only on the main issue here just to wrap my head around it.
How is something like this done "the React way"?

passing parameters through react-router

I have a code which displays four images and when one image is clicked out of those, it takes up the whole screen.I am using react router to implement the same.
The code is like this
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import {FirstPage} from './FirstPage.js';
import {Panorama} from './Panorama.js';
import {BrowserRouter,Route,Router,Switch} from 'react-router-dom';
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div>
<BrowserRouter>
<Switch>
<Route path="/" component={FirstPage} />
<Route path="/image/:id" component={Panorama} />
</Switch>
</BrowserRouter>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('container'));
FirstPage.js
import React from 'react';
import ReactDom from 'react-dom' ;
import $ from 'jquery' ;
import {Panorama} from './Panorama.js';
import {Redirect} from 'react-router-dom';
class FirstPage extends React.Component{
constructor(props){
super(props);
this.state={
list:[],
images:[],
isClicked:false,
redirect:true
}
this.loadImages=this.loadImages.bind(this);
this.loadOne=this.loadOne.bind(this);
}
componentDidMount(){
window.addEventListener('load',this.loadImages);
}
loadImages(){
console.log("load");
var that=this;
$.ajax({
type:'GET',
url:'https://demo0813639.mockable.io/getPanos',
datatype:'jsonp',
success:function(result){
var images=that.state.images;
for(var i=0;i<result.length;i++){
that.state.images.push({"pano":result[i].pano,"name":result[i].name});
}
that.setState({
images:images
})
}
})
}
loadOne(pano){
this.setState({
isClicked:true,
imageUrl:pano
})
}
render(){
var list=this.state.list;
return this.state.isClicked?<Redirect to={'/image/${this.state.imageUrl}'}/>:
<div> {this.state.images.map((result)=>{
return(<div className="box">
<div className="label">{result.name}</div>
<img src={result.pano} className="image col-md-3" onClick={this.loadOne.bind(this,result.pano)}/>
</div>
)
})}
</div>
}
}
module.exports={
FirstPage:FirstPage
}
Panorama.js
import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import {Link} from 'react-router-dom';
class Panorama extends React.Component{
render(){
return(
<div>
<div className="pano">
<img id="myImage" src={props.match.params.id} />
<div className="goback"><Link to="/">Go back</Link></div>
</div>
)
}
}
module.exports={
Panorama:Panorama
}
With this the URL becomes something like this
http://localhost:8080/image/$%7Bthis.state.imageUrl%7D
after clicking on an image and nothing comes up on the screen.With this,
Redirect to={{pathname='/image',params:{this.state.imageUrl}}}
I get syntax errors
What is the correct way of doing this?
It looks like you are using single quotes instead of back-ticks to produce the string inside your to prop.
Try changing
<Redirect to={'/image/${this.state.imageUrl}'}/>
to:
<Redirect to={`/image/${this.state.imageUrl}`}/>
are you using react-router 3 i recommend you to use redirect function provided by react-router 3, that is this.context.router.push() to start redirect.
sample code in component :
class FirstPage extends React.Component{
constructor(context)
{
super(context)
}
...
}
FirstPage.contextTypes = {
router: PropTypes.object.isRequired
}
sample redirect action in react-router 3 :
this.context.router.push({ pathname: `/image${this.state.imageUrl}`})
sample redirect action in react-router 4 :
this.context.router.history.push({ pathname: `/image${this.state.imageUrl}`})

Categories

Resources