Im currently trying to send a state from the Link component to the Router component.
The router component looks like this:
<Route
exact
path='/banner/edit/:id'
>
{!user && <Redirect to="/login" />}
{user && org && <EditBanner uid={user.uid} org={org} />}
</Route>
This component is only rendered when the user is not null, and when the org is not null.
The EditBanner has a state called: bannerTitle:
export const EditBanner = ({ uid, org }) => {
// States
const [bannerTitle, setBannerTitle] = useState(null);
...
And when I am using Link to go to the component, I want to pass in the bannerTitle as a property to set the state in the EditBanner component. However, this is not having any effect on the Component.
<Link
to={{
pathname: "/banner/edit/" + banner.id,
state: { bannerTitle: "HELLO" }
}}
>
Edit
</Link>
Where can I get the passed props?
You can use the useLocation hook which provides a location object which has state:
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import {useLocation} from "react-router-dom";
export const EditBanner = ({ uid, org }) => {
let location = useLocation();
useEffect(() => {
console.log(location.state);
}, [location])
// ...
}
Hopefully that helps!
Related
I want to build a page when from list of products I want to see product by ID. In my App file I have something like that:
<Route path={ROUTES.product} element={<Product id={1} />}>
but I want to replace static id with the value that comes from the selected product. In my Product file I have redirection to page with product but I don't know how I can pass the ID.
onClick={() => { navigate(`/products/${product?.id}`)}}
Any help would be appreciated.
The code you've provided appears to pass the id value in the path. It seems your question more about getting the Product component to have the correct id prop passed to it.
Given: path is "/products/:id"
Options to access the id param:
Use the useParams hook in Product to read the id route path param. This only works if Product is a function component.
import { useParams } from 'react-router-dom';
...
const { id } = useParams();
...
<Route path={ROUTES.product} element={<Product />} />
Use a wrapper component to use the useParams hook and inject the id value as a prop. This is useful if Product is not a function component.
import { useParams } from 'react-router-dom';
const ProductWrapper = () = {
const { id } = useParams();
return <Product id={id} />
};
...
<Route path={ROUTES.product} element={<ProductWrapper />} />
Create a custom withRouter Higher Order Component to use the useParams hook and inject a params prop.
import { useParams, ...other hooks... } from 'react-router-dom';
const withRouter = Component => props => {
const params = useParams();
... other hooks...
return (
<Component
{...props}
params={params}
... other hooks ...
/>
);
};
...
Wrap Product with withRouter HOC and access id param from props.params
props.params.id // or this.props.params
...
export default withRouter(Product);
...
<Route path={ROUTES.product} element={<Product />} />
so you already have the id with this
navigate(`/products/${product?.id}`)
just in Product component you can access id with
const { id } = useParams();
if you need to pass extra data you can try:
navigate(`/product/${product?.id}`, { state: { someExtradata }};
that you can access state in the component with :
const { state } = useLocation();
onClick={ () => Navegar(id) }
function Navegar(id) {
navigate('/products/id)
}
On pressing the browser back button why does an empty-blank page is displayed instead of the component that I'd visited before? Only the URL is getting changed to the previous one. Using React Router v5
That is really frustrating, how can I fix this ?
SignUp.js
render() {
return (
<div className='signUp-div'>
<Header />
<Router history={history}>
<div className='form-div'>
<Redirect to='/signup/mobile' /> // Default page
<Switch>
<Route exact path={'/signup/mobile'} component={MobileNum} />
<Route exact path={'/signup/idnumber'}>
<IdentNumber setPersonalID={this.props.setUserNumber} />
</Route>
<Route exact path={'/signup/password'}>
<CreatePass
setIfSignUp={this.props.setIfSignUp}
/>
</Route>
</Switch>
</div>
</Router>
</div>
);
}
IdentNumber.js
const IdentNumber = ({setPersonalID}) => {
const handleCheckID = () => {
history.push('/signup/password');
}
return (
<div className='form-div'>
<button
onChange={(event) => onChangeHandler(event)}
> Page password</button>
</div>
);
};
export default IdentNumber;
Did I explain it right ?
Thanks
From the code sandbox link, I've observed a few things that could potentially cause this issue.
Update your imports from import { Router } from "react-router-dom";
to
import { BrowserRouter as Router } from "react-router-dom";
<BrowserRouter> uses the HTML5 history API (pushState, replaceState and the popstate event) to keep your UI in sync with the URL.
The routes will remain the same. You're using react-router-dom v5.2.0, you could use useHistory to get the history object. useHistory simplified the process of making components route-aware.
With my changes: https://codesandbox.io/s/suspicious-pine-5uwoq
We don't need exact key for all routes other than the default "/" when it is enclosed in Switch and placed in the end. But exact matches /signup/mobile and /signup/* as same. Switch renders only one route and whichever route is matched first.
An example project for reference.
And if you want to handle the back button event yourself, follow the below examples.
In a function component, we can handle the back button press by listening to the history object.
import { useHistory } from 'react-router-dom';
const Test = () => {
const history = useHistory();
useEffect(() => {
return () => {
if (history.action === "POP") {
}
};
}, [history])
}
listen to history in useEffect to find out if the component is unmounted. history.listen lets us listen for changes to history.
Example:
import { useHistory } from 'react-router-dom';
const Test = () => {
const history = useHistory();
useEffect(() => {
return history.listen(location => {
if (history.action === 'POP') {
}
})
}, [])
}
react-router-dom now has Prompt,
import { Prompt } from "react-router-dom";
<Prompt
message={(location, action) => {
if (action === 'POP') {
// back button pressed
}
return location.pathname.startsWith("/test")
? true
: `Are you sure you want to go to ${location.pathname}?`
}}
/>
I take data from the resource thememoviedb. I received a certain number of movies, and I also displayed them on the page. I try to go to each movie to see the details in the file Movie_Details.js. In this file, I get data from the resource as well, but the transition does not occur, and the Movie_Details component is drawn at the bottom. How do I go to the Movie Details page and display the data? Thank you
App.js
import {BrowserRouter as Router, Switch, Route, Link} from "react-router-dom";
import {Popular_Movies} from "./Components/Popular_Movies/Popular_Movies";
import {Movie_Detail} from "./Components/Popular_Movies/Movie_Detail";
function App() {
return (
<div>
<Router>
<Popular_Movies/>
<Route path="/movie/:id" exact>
<Movie_Detail/>
</Route>
</Router>
</div>
);}
export default App;
Popular_Movie.js
import React from "react";
import {image_api} from "../../Services/Service";
import {Link} from "react-router-dom";
export let Popular_Movie = ({name, title, poster_path, vote_average}) => {
return (<div className='different_movie'>
<img src={image_api + poster_path} className='image'/>
<div className='bottom_different_movie'>
<h3> <Link to={`/movie/:id`}>{name} {title} {vote_average}</Link></h3>
</div>
</div>) }
Popular_Movies.js
import React, {useEffect, useState} from "react";
import {Popular_Movie} from "./Popular_Movie";
export let Popular_Movies = () => {
let popularMovieUrl = `https://api.themoviedb.org/3/tv/popular?api_key=e12a044061e5fe9077c7aee8a5165126&language=en-US&page=1`;
let fetchMovies = async () => {
let response = await fetch(popularMovieUrl)
let data = await response.json()
setPopularMovie(data.results)
}
useEffect(() => {
fetchMovies()
},[])
return (<div>
{popularMovie.length > 0 && popularMovie.map(movie => <Popular_Movie key={movie.id} {...movie}/>)}
</div>) }
Movie_Detail
import React, {useEffect, useState} from "react";
export let Movie_Detail = () => {
let movieUrl = 'https://api.themoviedb.org/3/tv/popular?api_key=e12a044061e5fe9077c7aee8a5165126&language=en-US&page=1'
let [differentMovie, setDifferentMovie] = useState([])
let fetchDifferentMovie = async () => {
let response = await fetch(movieUrl)
let data = await response.json()
setDifferentMovie(data.results)
}
useEffect(() => {
fetchDifferentMovie()
},[])
return (<div>
{differentMovie.title}
</div>)}
If you want to show movie list on / route, and movie details on /movie/:id you should have your router structured like this:
<Router>
... // put navigation component here
<Switch>
<Route path="/" exact>
<Popular_Movies/>
</Route>
<Route path="/movie/:id" exact>
<Movie_Detail/>
</Route>
</Switch>
</Router>
Inside Movie_Detail component you need to extract movie id by using the useParams hook
const { id } = useParams();
... // fetch movie details using `id`
It's important for you to understand how Switch operates. It's like you have if-else logic which shoud decide which component to render when certain path is matched. Please, read more on Basic Routing.
P.S. The reason you're seeing both popular movies and movie details underneath is because your Popular_Movies component call was not wrapped with <Route> which means that it should be always present/visible.
I have an app that is divided in 3 sections, a Header, Middle and Popular
<>
<Header />
<Middle />
<Popular />
</>
Header has a button that says 'Book it now' <Link to="/booking"> <button> Book it now </button> </Link>. When clicked, it uses React Router to switch route in the Middle component.
The 'Middle' component:
<Switch>
<Route path="/booking"> //When 'Book it now' is clicked, this is displayed.
<Booking />
</Route>
<Route path="/">
<Form />
</Route>
</Switch>
I would like to now make that Header button disappear when the route is '/booking'. I could create state and switch it to false after the button is clicked to not display the button, but I was thinking it would be pretty handy if I could do something like this in the Header file:
if (route === '/booking') {
//don't display button in Header (also, how would I do this?)
}
You can use withRouter imported from react-router-dom
import { withRouter } from 'react-router-dom';
const OtherComponent = withRouter(props => <MyComponent {...props}/>);
class MyComponent extends Component {
render() {
const { pathname } = this.props.location;
if(pathnanme != 'booking') {
<button>Hidden on booking</button>
}
}
}
That should be your Booking component:
import { withRouter } from 'react-router-dom';
const Booking = (props) => {
const { pathname } = props.location;
console.log(pathname);
return (
<div>
{(pathname !== 'booking' && pathname !== '/booking') ? <button>Hidden on booking</button> : null}
<span>always shown</span>
</div>
);
}
export default withRouter(Booking)
Hope this helps you
If you are using react-router v5 then I like to use hooks:
let location = useLocation();
let isBookingPage = location.pathname.includes('booking')
<>
{!isBookingPage && <Header />}
<Middle />
<Popular />
</>
By using the React Router 'useLocation' hook, you can have access to the route at which the user is currently at:
import { useLocation } from 'react-router-dom'
const location = useLocation() //with no arguments
console.log(location.pathname) //e.g. '/' or '/booking'
The location can the be used to conditionally render anything, in any component, depending on the route the user is at
location.pathname === '/booking' && <button> Book it now </button>
i am having route where i pass id,but i dont want to show id in url,
`<Route path={`${match.url}invite-members/:groupID`} exact component={InviteMembers} />`
this gets converted in url https://local..../invite-members/5,
but instead of that i want https://local..../invite-members, but the functionality should remain the same as in i get id in invite-members through this.props.match.params.groupID should be as it is,please help
using react router "react-router-dom": "^4.2.2",
If you want to change url to '/invite-members', you can add the Redirect component. And in case you want to save groupId, you could save it to your component state:
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import {
Router,
Route,
Link,
Switch,
Redirect
} from "react-router-dom";
class Root extends PureComponent {
// add groupId field to your component
// In case you use redux or any another state management library, you can save groupId to store
state = { groupId: null };
render() {
const { store, history } = this.props;
// just for example I defined '/' path to redirect on /invite-members url
return (
<Router>
<Switch>
<Route
path="/"
exact
render={props => (
<Redirect
to={{
pathname: "/invite-members/123",
state: { from: props.location }
}}
/>
)}
/>
<Route
path="/invite-members"
exact
render={props => (
<InviteMembers {...props} groupId={this.state.groupId} />
)}
/>
<Route
path="/invite-members/:groupID"
exact
render={props => {
return (
<RedirectAndSaveGroupId
{...props}
groupId={props.match.params.groupID}
onSetGroupId={groupId => {
this.setState({ groupId });
}}
/>
);
}}
/>
</Switch>
</Router>
);
}
}
export default Root;
class RedirectAndSaveGroupId extends PureComponent {
componentDidMount() {
// save groupId to Root component
this.props.onSetGroupId(this.props.groupId);
}
render() {
// redirect to /invite-members without groupId
return (
<Redirect
to={{
pathname: "/invite-members",
state: { from: this.props.location }
}}
/>
);
}
}
// Just for demo. In this.props.groupId we can receive groupId
class InviteMembers extends PureComponent {
render() {
return this.props.groupId;
}
}
Note, that in case you using any state management library such as Redux, you can store group id in them
I maybe have a very simple solution :
Router link :
<Link to={{pathname: '/item/'+name, state : {id}}}>{name}</Link>
In the Targeted file :
state = this.props.location.state
QueryParameters = () => {
const id = this.state.id
return { id }
}
And launch your query requiring the ID. It does not appear in the url.
Passing data in the params object will always result in that data being shown in the URL. Because the params object is built from the url.