0

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;

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks, yep this is what I have tried to do, replace 'id' with 'title' but I realise that in FullPost.js, the page that the push links to uses the same id to pick up the data so it breaks that... this line in FullPost.js gets affected... axios.get( '/posts/' + (this.props.match.params.id - 1) + '.json' )
I have just updated the answer. You will have the 'id' in the FullPost.js and you can make the API calls based on the id there.
I think we're getting there, looks like I might need to pass the id into the postSelectedHandler too? i.e. 'postSelectedHandler = (title, id) =>' For the const in the FullPost.js, should it be placed in the component Did Mount like so? i.e. componentDidMount () { const { location: {id} } = this.props; this.loadData(); }
You need that 'id' in the 'loadData()' function. So put the constant in the loadData() function. And replace all the occurrences of 'this.props.match.params.id' with 'id'.
There's just one thing I've noticed, when I hit refresh on the FullPost.js page, the id that got pushed disappears, is there a way of retaining that?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.