Reputation: 107
I've done a tutorial on setting up dynamic nested routes using posts as an example. However, the example only showed how to use :id in the url, I want to be able to use :title from my database instead.
So right now I am able to get this url when I click on an item from the list of posts from /posts: '/posts/1' which comes from '/posts/:id'.
However, I want to be able to show '/posts/ramen' in the url when the ramen post is selected. I tried changing the 'id' to 'title' for the 'this.props.history.push' but this seems to affect the way that FullPost.js uses that id to pick up the correct path for the data retrieval via Axios.
Below is my code:
My data is set up like this: posts.json
posts: 0: title: "ramen" id: 1 1: title: "udon" id: 2 2: title: "sushi" id: 3`
Posts.js: This is a list of posts, where each post is selectable
import React, { Component } from 'react';
import axios from '../../../axiosPosts';
import Aux from '../../../hoc/Aux/Aux';
import classes from './Posts.css';
import Post from '../../../components/Post/Post';
class Posts extends Component {
state = {
posts: []
}
componentDidMount () {
this.getData(this.props.pathname, this.props.filter);
}
getData(pathname, filter) {
axios.get(pathname + '.json')
.then(response => {
const post = response.data.filter(({category}) => category === filter);
const updatedPosts = post.map(post => {
return {
...post
}
});
this.setState({
posts: updatedPosts
});
})
.catch(error => {
console.log(error);
});
}
postSelectedHandler = ( id ) => {
this.props.history.push( this.props.match.url + '/' + id );
}
render () {
let posts = <p style={{textAlign: 'center'}}>Whoops! Something went wrong.</p>;
if(!this.state.error) {
posts = this.state.posts.map(post => {
return (
<Post
key={post.id}
title={post.title}
clicked={() => this.postSelectedHandler( post.id )} />
);
});
};
return (
<Aux>
<div className={classes.PostList}>
<h2 className={classes.PostListTitle}>{this.props.filter}</h2>
{posts}
</div>
</Aux>
)
}
}
export default Posts;
FullPost.js - This is the page that loads up when a post is selected
import React, { Component } from 'react';
import axios from '../../../axiosPosts';
import classes from './FullPost.css';
class FullPost extends Component {
state = {
loadedPost: null
}
componentDidMount () {
this.loadData();
}
loadData() {
if ( this.props.match.params.id ) {
if ( !this.state.loadedPost || (this.state.loadedPost && this.state.loadedPost.id !== +this.props.match.params.id) ) {
axios.get( '/posts/' + (this.props.match.params.id - 1) + '.json' )
.then( response => {
this.setState( { loadedPost: response.data } );
} );
}
}
}
render () {
let post = <p style={{ textAlign: 'center' }}>Please select a Post!</p>;
if ( this.props.match.params.id ) {
post = <p style={{ textAlign: 'center' }}>Loading...!</p>;
}
if ( this.state.loadedPost ) {
post = (
<div className={classes.FullPost}>
Content
</div>
);
}
return post;
}
}
export default FullPost;
Upvotes: 0
Views: 3090
Reputation: 715
You can just pass the 'title' of the post to the 'postSelectedHandler' function instead of 'id' of the post in your Posts.js.
<Post
key={post.id}
title={post.title}
clicked={() => this.postSelectedHandler( post.title, post.id )}
/>
And your 'postSelectedHandler' function will be like:
postSelectedHandler = ( title ) => {
const URL = `${this.props.match.url}/${title}`;
this.props.history.push({pathname: URL, id: id });
}
Access this id in the FullPost.js as:
const { location: {id} } = this.props;
In your 'routes' you can change the '/posts/:id' route with '/posts/:title' route.
This route change is only for your significance so that if someone else sees your code they will understand it easily that you are using the title of the post as route parameter.
Upvotes: 1