How to pass state to Link from reach-router - reactjs

UPDATED CODE STILL NOT WORKING
Haven't been able to find an answer on here to help with my problem.
In my Playlist component, I have an image that links to a new path for tracks. I want to pass a Playlist ID to the track so it can display tracks based on the ID it is given but no matter how I do this it wont work for me.
My Playlist Component returns:
<Link to = '/tracks' state = {{playlist:playlist.id}}>
<img alt = "album"src={playlist.images.url}/>
</Link>
In my Track Component, it is doing the following:
export default class Tracks extends React.Component {
constructor() {
super();
this.state = {
playlistid: 0
};
}
componentDidMount() {
this.setState({
playlistid: this.props.location.state.playlistid
});
}
render() {
return (
<div>
<p>PlaylistID: {this.state.playlistid}</p>
</div>
);
}
}
Can anyone tell me which Component is wrong? Sorry I'm still quite new to React
Thanks

In order for you to pass on a state to Link component from Reach router, you can pass on state as a separate prop. The to prop neeeds to be a string
<Link
to='/tracks'
state= {{
playlistid: playlist.id
}}
>
<img alt = "album"src={image.url}/>
</Link>
For more information refer to the documentation of Link from Reach-router
EDIT:
The way you have implemented the Link component works with react-router Link component

You can try this :
<Link to={{
pathname: `/tracks/${playlist.id}`, //I assume you tracked the tracklist state here as a props
}}>
<img alt = "album"src={image.url}/>
</Link>

Related

How to transfer state through Link in react-dom 5.0?

I develop react-app for the first time in my career.
So, I wander about a lot of things...haaa
I try to pass state between componet through Link element
But I did typing another variable syntax.
But I always see state: null on the console.
And There are many error message 'can't read state'
<Link to={{pathname:"/movie-detail", state:{year:year, title:title, summary:summary, poster:poster, genres:genres}}}>~~~</Link>
<Link to={"/movie-detail"} state={{year:year, title:title, summary:summary, poster:poster, genres:genres}}>~~</Link>
But I can't move state recipient component.
Please teach me that what is wrong with my code...
Hading over code.
import React from 'react'
import PropTypes from 'prop-types';
import './Movie.css';
import { Link } from 'react-router-dom';
function Movie({year, title, summary, poster, genres}){
return (
<div className="movie">
<Link to={"/movie-detail"} state={{year:year, title:title, summary:summary, poster:poster, genres:genres}}>
<img src={poster} alt={title} title={title}/>
<div className="movie__data">
<h3 className="movie__title" style={{backgroundColor:'red'}}>{title}</h3>
<h5 className="movie__year">{year}</h5>
<ul className="movie__genres">
{genres.map((genres, index) => {
return (
<li key={index} className="movie__genre">{genres}</li>
);
})}
</ul>
<p className="moive__summary">{summary.slice(0,180)}...</p>
</div>
</Link>
</div>
);
}
Movie.propTypes = {
id:PropTypes.number.isRequired,
year:PropTypes.number.isRequired,
title:PropTypes.string.isRequired,
summary:PropTypes.string.isRequired,
poster:PropTypes.string.isRequired,
genres:PropTypes.arrayOf(PropTypes.string).isRequired,
}
export default Movie;
Recipient Code
import React from 'react';
class Detail extends React.Component{
componentDidMount(){
const{location, history} = this.props;
//console.log(this.props);
if(location.state === undefined){
history.push('/');
}
}
render() {
const{location} = this.props;
if(location.state){
return(
<span>{location.state.title}</span>
);
}else{
return null;
};
}
}
export default Detail;
Link component has a to prop which can be string, object and function. So the correct code is:
<Link to={{ pathname: '/movie-detail', state: { year: year, title: title, summary: summary, poster: poster, genres: genres } }}>~~~</Link>
Try using withRouter HOC wrap your Detail component so that you can get the location and history objects from the props.
I would not recommend using your router to pass data.
Ideally you'll want to fetch data from within your details page based off of the given route
(e.g. /movie/star-wars fetches data for post "star-wars" from your server). By separating your router and component logic you maintain your application's modularity.

React Component crashes on reload

I have a notes-list component that gets the notes data as props from the main component.
Inside the notes-listcomponent, there is a notes-item component which has a dynamic route that loads notesItem-page. So, for every notes-item there is a dynamic url for it's respective notesItem-pagewhich has all the details about the notesItem object. I use Link from react-router-dom
The notes-list component looks like this:
export class NotesList extends Component {
constructor(props) {
super(props);
}
render() {
const { isLoggedIn } = this.props.loggedInContext;
return (
<div className="notes-list">
{this.props.notes.map((notesItem) => (
<Link
to={{
pathname: `${notesItem.slug}`,
id: notesItem._id,
}}
style={{
textDecoration: "none",
color: "#fea82f",
}}
>
<NotesItem notes={notesItem} />
</Link>
))}
</div>
);
}
}
export default loggedInContext(NotesList);
This successfully redirects me to the NotesItem-page with the correct props and inside the notesItem-page I get the receive the id of the object that I had passed as props and make an API call with that particular id in ComponentDidMount() method.
This works perfectly. However, it crashes on reload. It gives the following error:
I am guessing it is because of ComponentDidMount works only once,but I do not seem to find an alternate solution to this problem.
The notesItem-page component looks like this:
export class notesItemPage extends Component {
constructor(props) {
super(props);
this.state = {
notesItem: [],
};
}
componentDidMount() {
fetch(`http://localhost:5000/api/v1/notes/fetch/${this.props.location.id}`)
.then((notesItem) => notesItem.json())
.then((notesItem) =>
this.setState({ notesItem: notesItem.data, isLoaded: true })
);
}
render() {
const { notesItem } = this.state;
return (
<div className="notesItem-details">
<h1> {notesItem.title} Notes</h1>
</div>
);
}
}
export default notesItemPage;
It would be great if anyone could help me with this, thanks!
Issue is here:
<h1> {notesItem.title} Notes</h1>
here the notesItem is coming from an axios call and this data is not available on the time of first component render and this is causing the issue ( app crash ).
So change this:
<h1> {notesItem.title} Notes</h1>
to
<h1> { notesItem && notesItem.title } Notes</h1> // use it when it is available from axios call

How to pass data to functional component?

As part of React course I'm making a dummy eshop and want to pass products from state of one component to functional component and display product properties there. How to pass data from state to stateless component?
I've set up a component ProductList which stores the products (fetched from api) and ProductDetail which is supposed to show the details (find the product by id and show it's descr, img etc).
I've set up App.js which has two Routes - to List and to Detail.
App.js:
class App extends Component {
render() {
return (
<div>
<h1>E-Commerce app</h1>
<Route exact path="/" component={ProductList} />
<Route path="/:productId" component={ProductDetail} />
</div>
)
}
}
ProductList.js:
class ProductList extends Component {
state = {
isLoading: true,
products: [],
}
async componentDidMount() {
const products = await getProducts()
this.setState({ products: products.data, isLoading: false })
}
render() {
const { isLoading, products } = this.state
return (
<div>
{isLoading && '...'}
{products && (
<ul>
{products.map(item => (
<li key={item.id}>
<Link to={`${item.id}`}>
<h2>{item.attributes.name}</h2>
<img
src={item.attributes.image_url}
width="60"
alt={item.attributes.description}
/>
</Link>
</li>
))}
</ul>
)}
</div>
)
}
}
ProductDetail.js:
const ProductDetail = ({ match }) => {
return (
<div>
<p>Product ID: {match.params.productId}</p>
<img src="" alt="" />
<p>Product name: </p>
<p>Product description: </p>
</div>
)
}
Your data is in the ProductList component's state, and as i have understood you want to display one product detail in another component after clicking the link, right ?
When switching the route your component will unmount and therefore you lose data.
In order to do that you need to do some state management like Redux, MobX or other state management libraries or you can use the Context API which is a feature that has been introduced lately in React.
You will need another request on the details page /:productId.
Keep in mind that if a user go straight to the /:productId route the product data will not exist, this is why you need a request here.
To optimize you can use Context, or a lib like Redux to manage/store the data. So you can check first if the data of productId exists in the store before doing a new request.
First change Link like below
<Link to={{
pathname: `${item.id}`,
state: {
productObject: item
}
}}>
Then change ProductDetail component params.
const ProductDetail = ({ match,location })
Finally read Product Name like this.
<p>Product name:{location.state.productObject.attributes.name} </p>
Let me know if it helps.
Thanks

sending props via the react-router Link component

I have a problem with sending props through the Link component of the react-router.
This is my component that is responsible for displaying a specific element:
const ItemRecipe = ({ recipe }) => {
const { label, image } = recipe.recipe;
return (
<li>
<p> {label} </p>
<img src={image} alt="food" />
<Link to={{pathname: `/meals/${label}`, params: recipe }}>
<p>next</p>
</Link >
</li>
);
}
After clicking on I want to open the page with a specific recipe. This component looks like this
class DetailsRecipe extends Component {
constructor(props) {
super(props);
this.state = {
recipe: props.match.params.recipe
}
console.log(this.state.recipe);
}
render () {
<div>
lorem lorem
</div>
)
}
}
console.log(this.state.recipe) displays undefined.
how to fix this error? How to correctly send data through the Link component of the react-router?
I looked at similar topics on a stackoverflow but it did not help to solve my problem
Have another look at the documentation of the Link component. The properties allowed inside the to object are:
pathname: A string representing the path to link to.
search: A string representation of query parameters.
hash: A hash to put in the URL, e.g. #a-hash.
state: State to persist to the location.
I guess you want to use state instead of params.
EDIT:
So your code would look like this:
<Link to={{pathname: `/meals/${label}`, state: {"recipe": recipe} }}>
Then you can access the state inside the linked component like this:
this.state = {
recipe: props.location.state.recipe
};

Pass props to React Router's Link Component

I'm passing location props to React Router's Link. It successfully navigates me away, but when I click "back" to go back to the previous page, I get an error:
TypeError Cannot Read property "statusUser" Undefined
It looks like the props are getting messed up when I navigate back.
Sidebar.js
Class Sidebar extends Commponent{
render(){
return(
<Link to={
{
pathname: `/user`,
state:{
statusUser: 'abc',
}
}
}><Button>User Menu</Button>
}
)
}
User.jsx
class user extends Component {
render(){
return(
<UserTable status={this.props.location.state.statusUser}/>
)
}
}
UserTable.jsx
class UserTable extends Component{
render(){
return(
<Link to={
{
pathname: `/user/detail/${this.props.status}`,
state:{
statusUser: this.props.status,
}
}
}><Button>Detail</Button>
)
}
}
UserDetail.jsx
class UserDetail extends Component{
render(){
return(
<p>{this.props.location.state.statusUser}</p>
<Link to={
{
pathname: `/user`,
}
}><Button>Back</Button>
)
}
}
I think the problem is in User.js. How do I make the props fixed like using setState or stuff like that inside User.js file?
Just to be able read the props property, and after reading it go back to the previous page by clicking "back" button.
I'm sorry I just really really new to React so any help would be really appreciate. Thank you.
I believe you're getting that error when you click the Back button in UserDetail.jsx. You aren't passing a state object to User.jsx.
The User.jsx component tries to access this.props.location.state.statusUser, but state doesn't exist, and so it says it can't read the property statusUser on undefined (state is undefined).
Here's what you're missing, with a few typos fixed:
// UserDetail.jsx
class UserDetail extends Component {
render() {
return (
<div>
<p>{this.props.location.state.statusUser}</p>
<Link
to={{
pathname: `/user`
// (You aren't passing `state`)
}}
>
<Button>Back</Button>
</Link> // <-- You weren't closing Link
</div>
);
}
}
// User.jsx
class User extends Component { // <-- Capital 'U'
render() {
return (
<UserTable
status={this.props.location.state.statusUser} // <-- You require `state`
/>
);
}
}
As for how to fix it, the easiest way would be to pass a state object with your "Back" button in UserDetail.jsx.
You can also set defaultProps in React. I'm not sure if that's applicable to you, but it lets you provide a fallback for such things:
Edit: Include example of defaultProps and propTypes.
// User.jsx
class User extends Component {
render() {
return (
<UserTable
status={this.props.location.state.statusUser}
/>
);
}
}
// Maybe something you put in a different file
const defaultLocation = {
state: {
statusUser: '', // <-- Your default statusUser
},
};
User.defaultProps = {
location: defaultLocation,
};
// import PropTypes from 'prop-types';
User.propTypes = {
location: PropTypes.shape({
state: PropTypes.shape({
statusUser: PropTypes.string.isRequired,
})
})
};
I added PropTypes in the above example. It's a way to set checks for the data you're requiring in your components.
Typically, we don't put props into defaultProps if it isRequired, though, because it will never come to that. If you're unsure what default value to give statusUser, I'd recommend starting with making it required in propTypes, and then you can refactor in the future if the need arises.
There were some bugs with your other code samples, too. I've formatted them below, and fixed the bugs. I'll point out what I fixed in comments:
Sidebar.js
class Sidebar extends Component { // <-- Lowercase 'c'; "Component"
render() {
return (
<Link
to={{
pathname: `/user`,
state: {
statusUser: "abc"
}
}}
>
<Button>User Menu</Button>
</Link> // <-- You weren't closing Link
);
}
}
UserTable.jsx
class UserTable extends Component {
render() {
return (
<Link
to={{
pathname: `/user/detail/${this.props.status}`,
state: {
statusUser: this.props.status
}
}}
>
<Button>Detail</Button>
</Link> // <-- You weren't closing Link
);
}
}

Resources