I have a screen with buttons, common background and common title and changing nested components. Inside this screen I want to change nested components with a click of a button. Nested components must change each other in a circle with left and right button. So far I did a lots of attempts to achieve this ( I try to do it with withRouter), I give you code only of one of my attempts, but all of them didn't work. I don't get any errors, I see route in browser is changing but screen doesn't, I see only the first nested component. There is questions about this on SOF, but they related to older version of react-router.
Here my code, if you need more information feel free to ask in comments.
import React, { Component } from 'react';
import { Link,
BrowserRouter as Router,
Route,
Switch,
withRouter } from 'react-router-dom';
import Info1 from './info/info1';
import Info2 from './info/info2';
import Info3 from './info/info3';
import Info4 from './info/info4';
class Info extends Component {
constructor(props) {
super(props);
this.currentIndex = 1;
}
componentDidMount() {
}
leftHandler() {
console.log("left click");
var temp = this.currentIndex;
this.changeScreen(--temp);
}
rightHandler() {
console.log("right click");
var temp = this.currentIndex;
this.changeScreen(++temp);
}
changeScreen(index) {
const numberOfScreens = 4;
if(index < 1)
this.currentIndex = numberOfScreens;
else if(index > numberOfScreens)
this.currentIndex = 1;
else
this.currentIndex = index;
this.props.history.push("/info/" + this.currentIndex);
}
render() {
return (
<Router>
<div className="info-common">
<img className="game-title info-game"/>
<Switch>
<Route path="/info/1" component={ Info1 }/>
<Route path="/info/2" component={ Info2 }/>
<Route path="/info/3" component={ Info3 }/>
<Route path="/info/4" component={ Info4 }/>
</Switch>
<Link to="/rings"><button className="back-info-btn">назад</button></Link>
<button onClick={ this.leftHandler.bind(this) } className="left-info-btn"></button>
<button onClick={ this.rightHandler.bind(this)} className="right-info-btn"></button>
</div>
</Router>
);
}
}
Info.propTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired,
}).isRequired,
location: React.PropTypes.isRequired,
};
export default withRouter(Info);
EDIT:
While I accepted given answer, I didn't test it, in my project I used this solution:
app.js
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom';
...
render() {
return (
<div id='game-container' width="1236" height="634">
<Router>
<div>
<Route path="/info" component={ Info }/>
</div>
</Router>
</div>
);
}
Then in Info itself:
Info.js
class Info extends Component {
constructor(props) {
super(props);
this.currentIndex = 1;
}
leftHandler() {
console.log("left click");
var temp = this.currentIndex;
this.changeScreen(--temp);
}
rightHandler() {
console.log("right click");
var temp = this.currentIndex;
this.changeScreen(++temp);
}
changeScreen(index) {
const numberOfScreens = 4;
if(index < 1)
this.currentIndex = numberOfScreens;
else if(index > numberOfScreens)
this.currentIndex = 1;
else
this.currentIndex = index;
this.props.history.push("/info/" + this.currentIndex);
}
render() {
return (
<div className="info-common">
<img className="game-title info-game" src={ this.drawGame() }/>
<Switch>
<Route path={`${this.props.match.path}/1`} component={ Info1 }/>
<Route path={`${this.props.match.path}/2`} component={ Info2 }/>
<Route path={`${this.props.match.path}/3`} component={ Info3 }/>
<Route path={`${this.props.match.path}/4`} component={ Info4 }/>
</Switch>
<Link to="/rings"><button className="back-info-btn">назад</button></Link>
<button onClick={ this.leftHandler.bind(this) } className="left-info-btn"></button>
<button onClick={ this.rightHandler.bind(this)} className="right-info-btn"></button>
</div>
);
}
}
Info.propTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired,
}).isRequired,
location: React.PropTypes.object.isRequired,
};
export default withRouter(Info);
If you wrap a component in withRouter, you can only use it inside a <Router>, just like <Route>s etc.
To get your example working, you need to make <Info> a child of <Router>, since it uses withRouter. First, remove the <Router> from the render method, and just render the <div> as the top-level component:
render() {
return (
<div className="info-common">
<img className="game-title info-game"/>
<Switch>
<Route path="/info/1" component={ Info1 }/>
<Route path="/info/2" component={ Info2 }/>
<Route path="/info/3" component={ Info3 }/>
<Route path="/info/4" component={ Info4 }/>
</Switch>
<Link to="/rings">
<button className="back-info-btn">назад</button>
</Link>
<button onClick={ this.leftHandler.bind(this) } className="left-info-btn"></button>
<button onClick={ this.rightHandler.bind(this)} className="right-info-btn"></button>
</div>
)
}
Then, wherever you render <Info />, render <Router><Info /></Router> instead. Or, add an extra component that renders the two, and use that component instead of <Info />.
// Option 1: render <Router> wherever you use <Info>
import Info from './info';
...
ReactDOM.render(<Router><Info /></Router>);
// Option 2: add another component that wraps <Info> in a Router,
// either as the new export of the module, or as a new module
const App = () => (
<Router>
<Info />
</Router>
);
export default App;
Related
I am trying to develop react application. The problem is when I use the Route and Switch, it is not working. Actually, nothing is happening. Could anyone please give me a clue about the possible problem here?
Here is my code:
import React, { Component } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import { DISHES } from '../shared/dishes';
import DishDetailComponent from './DishdetailComponent';
import Header from './HeaderComponent';
import Footer from './FooterComponent';
import { Switch, Route, Redirect, BrowserRouter as Router } from 'react-router-dom';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
dishes: DISHES
};
}
render() {
const HomePage = () => {
return(
<Home />
);
}
return (
<div>
<Header />
<Router>
<Switch>
<Route path="/home" Component={HomePage} />
<Route path="/menu" Component={() => <Menu dishes={this.state.dishes} />} />
<Redirect to="home" />
</ Switch>
</Router>
<Footer />
</div>
);
}
}
The obvious mistake is that you have capitalized the "C" in the component prop, so you should call it like component={HomePage}
Here are some other things you could improve upon though:
If you are gonna use an inline function, it is preferable to use the render prop, and if you are gonna use a component directly, preferable to just use Component prop. Moreover:
const HomePage = () => {
return(
<Home />
);
}
is unnecessary as you can just use the Home component directly.
Try this for your render() function:
render() {
return (
<div>
<Header />
<Router>
<Switch>
<Route path="/home" component={Home} />
<Route path="/menu" render={() => <Menu dishes={this.state.dishes} />} />
<Redirect to="home" />
</ Switch>
</Router>
<Footer />
</div>
);
}
I need to get the current url from the browser address bar and set that value to a react state's currentPath.
So this is my code looks like:
import React, { Component } from "react";
import "./App.css";
import "./css/custom.css";
import {
BrowserRouter as Router,
Switch,
Route,
useLocation,
} from "react-router-dom";
import NavigationBar from "./pages/homepage-components/1-navbar";
import HomePage from "./pages/HomePage";
import Post from "./pages/Post";
function NavigationHeader() {
const location = useLocation();
return location.pathname;
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentPath: "",
};
}
componentDidMount() {
this.setState({
currentPath: NavigationHeader, //even tried NavigationHeader() and NavigationHeader.toString
});
}
render() {
return (
<Router>
{/* --------------- Navigation Bar --------------- */}
<NavigationBar />
Path is: {this.state.currentPath}
{/* --------------- End of Navigation Bar --------------- */}
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/post" component={Post} />
</Switch>
</Router>
);
}
}
export default App;
But it doen't update the currentPath because where it says Path is: {this.state.currentPath} it is not showing. Can someone help me, please?
Although setting currentPath is meaningless and your example is a bad practice you can just render your NavigationHeader with setLocation prop:
function NavigationHeader({ setLocation }) {
const location = useLocation();
React.useEffect(() => {
setLocation(location.pathname)
}, [location.pathname])
return null;
}
...
{/* --------------- Navigation Bar --------------- */}
<NavigationHeader setLocation={ (currentPath) => this.setState({ currentPath }) }/>
To make it more clean you can refactor your NavigationHeader so that it contain Path is: {location.pathname}:
function NavigationHeader() {
const location = useLocation();
return <p>Path is: {location.pathname}</p>;
}
function App() {
return (
<Router>
{/* --------------- Navigation Bar --------------- */}
<NavigationBar />
<NavigationHeader />
{/* --------------- End of Navigation Bar --------------- */}
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/post" component={Post} />
</Switch>
</Router>
);
}
You can get current url from window.
componentDidMount() {
this.setState({
currentPath: window.location.href
});
}
Hi I googled everywhere about my problem still no luck for me.
Here i attached my sample code.
import React from 'react';
import {
BrowserRouter,
Route,
Switch,
NavLink,
Redirect
} from "react-router-dom";
const Login = () => <div> Login Content <NavLink to ="/AppHome"> go to App</NavLink> </div>;
const Register = () => <div> Register Content </div>;
const AppHome = () => <div> Welcome to the App </div>;
class Authenticate extends React.Component {
render() {
return (
<>
<div>
<NavLink to={"/Login"}> Login </NavLink>
<NavLink to = {"/Register"} > Register < /NavLink>
</div>
<div>
<Switch >
<Route path={"/"} component={Login} />
<Route path={"/Login"} component={Login}/>
<Route path={"/Register"} component = {Register}/>
</Switch>
</div>
</>
);
}
}
class App extends React.Component {
render() {
return (
<BrowserRouter >
<Switch >
<Route path="/" component={Authenticate}/>
<Route path="/AppHome" component={AppHome}/>
</Switch>
</BrowserRouter>
)
}
}
export default App;
Here, In Localhost:3000 , i set Login Component as default to show. it shows the view but when clicking on the signup link, url only changing not the view. what am i done wrong?
You are trying to nest the routes, but in your case this seems unnecessary.
I would setup my routes like this without nested routing:
import React from "react";
import {
BrowserRouter,
Route,
Switch,
NavLink,
Redirect
} from "react-router-dom";
class App extends React.Component {
render() {
return (
<BrowserRouter>
<Authenticate />
<Switch>
<Route path="/" exact component={Login} />
<Route path="/Login" component={Login} />
<Route path="/Register" component={Register} />
<Route path="/AppHome" component={AppHome} />
</Switch>
</BrowserRouter>
);
}
}
const Login = () => (
<div>
Login Content <NavLink to="/AppHome"> go to App</NavLink>
</div>
);
const Register = () => <div> Register Content </div>;
const AppHome = () => <div> Welcome to the App </div>;
class Authenticate extends React.Component {
render() {
return (
<>
<div>
<NavLink to={"/Login"}> Login </NavLink>
<NavLink to={"/Register"}> Register </NavLink>
</div>
</>
);
}
}
export default App;
Codesandbox
import React from 'react'
import ReactDOM from 'react-dom'
import {HashRouter, Route, Switch, NavLink} from 'react-router-dom'
class App extends React.Component {
render() {
return (
<HashRouter>
<Switch>
<Route path="/Login" component={Login} />
<Route path="/Register" component={Register} />
<Route path="/" component={AppHome} />
</Switch>
</HashRouter>
)
}
}
const Login = () => (
<div>
Login Content <NavLink to="/AppHome"> go to App</NavLink>
</div>
)
const Register = () => <div> Register Content </div>
const AppHome = () => <div> Welcome to the App </div>
export default App
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Can you please check this Codesandbox Once.
I have these components. I want to turn every <House/> into a dynamic url. For example, when accessing in the browser, http://localhost:3000/houses/house/1
I want to appear the House 1.
The other things in the application are working fine. I just want to solve this problem of implementing dynamic routes.
Router Component
import React from 'react';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom'
import App from './App'
import Houses from './Houses'
export default props => (
<Router>
<Route exact path='/' render={() => <App />} >
<Route exact path='/houses' render={() => <Houses />} />
</Route>
</Router>
)
Houses Component
import React, { Component } from 'react'
import House from './House'
var data = require('./db.json');
class Houses extends Component {
constructor(props) {
super(props);
this.state = {
currentHouse: []
};
}
componentDidMount() {
this.setState({
currentHouse: data[0]
})
}
render() {
const {currentHouse} = this.state;
return (
<div className="content house">
<ul>
{currentHouse.photos && currentHouse.photos.map((photo, index) => {
return(
<House photo={photo} key={index}/>
)
})}
</ul>
</div>
)
}
}
export default Houses
House Component
import React from 'react';
function House(prop) {
return (
<li><img src={`/images/${prop.photo}`}/></li>
);
}
export default House;
<Route exact path='/houses/:id' render={(props) => <House {...props} />} />
and inside House component retrieve the id:
prop.match.params.id
Ref: https://scotch.io/courses/using-react-router-4/route-params
I have the following ProductThumbnail component where as i click on the link it updates the URL and changes the route to redirect toward ProductDesc component. It generates the URL /productDescRedux/itemId properly.
const ProductThumbnail = (props) => {
const itemId = props.product
return(
<div>
<Link to={`/productDescRedux/${itemId.id}`}>
<div>
<h1>{props.product.headline}</h1>
<img src={props.product.images[0].imagesUrls.entry[1].url} alt="Thumbnail small pic"/>
<p></p>
</div>
</Link>
</div>
)
}
ProductThumbnail.propTypes = {
product: React.PropTypes.object
};
export default ProductThumbnail;
However despite URL changes, it does not call the component ProductDesc and i have to reload the page to display the component ProductDesc. Below the routes and the component ProductDesc
const Routes = function(){
return(
<Provider store={store}>
<Router history={ hashHistory }>
<Route path="/" component={ Container }>
<IndexRoute component={ Home } />
<Route path="/productredux" component={ App } >
<IndexRoute component={ ProductsGrid }/>
<Route path="/productDescRedux/:id" component={ ProductDesc } />
</Route>
<Route path="*" component={ NotFound } />
</Route>
</Router>
</Provider>
)
}
export default Routes;
const ProductDesc = ()=>({
render(){
return (
<div>
<h1>hello</h1>
<p>Yeah</p>
</div>
)
}
})
And here for completion the App component which uses connect() as well as the Main component
function mapStateToProps(state){
return {
products:state.products
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators,dispatch);
}
const App = connect(mapStateToProps,mapDispatchToProps)(Main);
export default App;
//in a different file
const Main = (props) => ({
render(){
return (
<div>
{React.cloneElement(props.children, this.props)}
</div>
)
}
})
export default Main;
So I don't see why when changing the URL , routing is not calling the component ProductDesc. any insight ?
Syntax issue on the Main component which is the parent of all. I needed
{React.cloneElement(this.props.children, this.props)}
instead of
{React.cloneElement(props.children, this.props)}
Ref issue