2
votes

I have a react app that consists of a main Component. , which comprises a element with navbar markup.

//app.js
    import React, { Component } from 'react';
    import {
      Route,
      NavLink,
      BrowserRouter,
      Switch,
    } from "react-router-dom";

    import Home from './components/home';
    import Create from './components/create';
    import './App.css';

    const pathBase = 'http://127.0.0.1:8000';
    const pathSearch = '/posts/';

    class App extends Component {
      constructor(props) {
        super(props);

        this.state = {
          posts: [],
          post: null,
          error: null,
        }

      }


      setPostsState = result => {
        this.setState({ posts: result})
      }

      setPostState = post => {
        this.setState({ post: post});
        console.log(this.state.post)

      }

      retrievePost = (event, post) => {
        event.preventDefault();
        this.fetchPost(post);

      }

      deletePosts = id => {
        fetch(`${pathBase}${pathSearch}${id }`, { method: 'DELETE'})
        .then(res => res.status === 204 ? this.fetchPosts() : null)
      }

      editPosts = id => {
        fetch(`${pathBase}${pathSearch}${id }`, { method: 'PUT'})
        .then(res => res.status === 204 ? this.fetchPosts() : null)
      }

      fetchPost = (post) => {
        fetch(`${pathBase}${pathSearch}${post.id}`)
          .then(response => response.json())
          .then(result => this.setPostState(result))
          .catch(error => console.log(error));
      }

      fetchPosts = () => {
        fetch(`${pathBase}${pathSearch}`)
          .then(response => response.json())
          .then(result => this.setPostsState(result))
          .catch(error => this.setState({ error: error }));
      }

      componentDidMount() {
        this.fetchPosts();
      }


      render() {
        const { posts, error } = this.state;

        if (error) {
          return <div className="container">
                  <h3>Error: {error.message}</h3>
                </div>;
        }

        return (
          <BrowserRouter>
            <div className="container">
            <header>
              <nav className="navbar navbar-expand-md navbar-light bg-light">
                <a className="navbar-brand" href="">Django Rest API</a>
                <div className="collapse navbar-collapse" id="navbarSupportedContent">
                  <ul className="navbar-nav mr-auto">
                    <li className="nav-item active">
                      <NavLink to="/posts">Posts</NavLink>
                    </li>
                    <li className="nav-item">
                      <NavLink to="/create">Create</NavLink>
                    </li>
                  </ul>
                </div>
              </nav>
            </header>
            <Switch>

            <Route path="/create" 
                   render={(props) => 
                  <Create {...props}
                    fetchPosts={this.fetchPosts} 
                  />
                } 
            />

               <Route path="/posts" 
                render={(props) => 
                  <Home {...props} 
                    posts={posts} 
                    deletePost={this.deletePosts} 
                  />
                } 
            />

            </Switch>

          </div>
          </BrowserRouter>

        );
      }
    }

    export default App;

There is also a (using React-Router v4) which path="/posts" that renders a Component . This component consists of a list which maps over blog posts obtained from the DB. Each post displays as a card with the post name serving as a link, that when the user clicks should bring up the component to show Post details.

// components/home.js
import React, { Component } from 'react';
import Post from './post';
import {
    Route,
    NavLink,    
  } from "react-router-dom";

class Home extends Component {


    render() {
        const ulStyle = { listStyleType: 'none' }
        const { deletePost, posts } = this.props;
        const match = this.props;
        console.log(match)

        return (
            <ul style={ ulStyle }>
            <h3>Posts</h3>
            {posts.map(post =>
            <li 
                key={posts.indexOf(post)}
                value={post.id} 
            >
              <div className="card">
                <div className="card-body">
                    <NavLink to={`/posts/${post.id}`}>{post.title} 
  </NavLink>, Created: {post.timestamp}
                <button type="button"
                  onClick={() => deletePost(post.id)} 
                  className="float-right btn btn-outline-danger btn-sm"
                >
                  Delete
                </button>
                </div>

                </div>
              </li>


              )}

              <Route exact path={`/posts/:postId`} render={(props) => 
 (<Post {...props} posts={posts}/>)}/>   

          </ul>
        );
    }
}

export default Home;

I am having trouble getting the post component to render on a new page. Currently if the user clicks a link, the component renders at the bottom of the existing page (the component). I need the Post component to render on a entirely new page. How did I do this?

import React, { Component } from 'react';

class Post extends Component {

    render() {  
        const { children, match, posts} = this.props;
        console.log(match);
        console.log(posts);

        const p = (posts) => {
            posts.find(post => post === match.postId)
        }

        return (
                <div>
                    {match.params.postId}
                    <p>HELLO WORLD</p>
                </div>
         );
        }
    }


export default Post;
2

2 Answers

3
votes

What you've seen is an expected behaviour for React Router nested routes. Content of the nested route will be rendered as part of it's parent component.

It's designed this way so that the number of re-renderings will be kept to minimum when navigation (the content of the parent component will not be re-rendered when the nested routes changed).

If you want the whole page content to be changed, you should not use nested route. Simply put all the routes you have under the Switch component. In other words, move your single post route to App component.

0
votes

you should have a div to the router components. for example

<Router>
    <div className = "app">
     <nav />
      <div className = "container">
       //routes to be render here

     <Route exact path = "/sample" component = {Sample} />
      </div>
     <footer />
    </div>
   </Router>

so that the component will be render on the middle of the page and remove the current if the route changes.