GregH
GregH

Reputation: 5459

React rendering both components on partial route match

I am currently getting started with react and have two routes defined as such:

<Route path='/articles' component={ArticlesIndex} />
<Route path='/articles/create' component={ArticlesCreate} />

When visiting /articles, only my ArticlesIndex component is rendered as I'd expect. However, when navigating to /articles/create, both my ArticlesIndex and ArticlesCreate components are both being rendered on the page despite my ArticlesCreate component having no reference to ArticlesIndex. It seems as though the react router is rendering the ArticlesIndex component due to the route being contained in articles/create.

How can I overcome this?

For completeness' sake, Here are my two components:

ArticlesIndex component:

import React, { Component } from 'react'

export class ArticlesIndex extends Component {
  displayName = ArticlesIndex.name

  constructor(props) {
    super(props)
    this.state = { articles: [], loading: true }

    fetch('https://localhost:44360/api/Articles/')
      .then(response => response.json())
      .then(data => {
        this.setState({ articles: data, loading: false })
      })
  }

  static renderArticlesTable(articles) {
    return (
      <table className="table">
        <thead>
          <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          {articles.map(article => (
            <tr key={article.id}>
              <td>{article.id}</td>
              <td>{article.title}</td>
              <td>{article.description}</td>
            </tr>
          ))}
        </tbody>
      </table>
    )
  }

  render() {
    let contents = this.state.loading ? (
      <p>
        <em>Loading...</em>
      </p>
    ) : (
      ArticlesIndex.renderArticlesTable(this.state.articles)
    )

    return (
      <div>
        <h1>Articles</h1>
        {contents}
      </div>
    )
  }
}

and ArticlesCreate component:

import React, { Component } from 'react'

export class ArticlesCreate extends Component {
  displayName = ArticlesCreate.name

  constructor(props) {
    super(props)
    this.state = {
      title: '',
      description: '',
    }

    this.handleSubmit = this.handleSubmit.bind(this)
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <div className="form-group row">
          <label className=" control-label col-md-12" htmlFor="Title">
            Title
          </label>
          <div className="col-md-4">
            <input
              className="form-control"
              type="text"
              name="title"
              defaultValue={this.state.title}
              required
            />
          </div>
        </div>
        <div className="form-group row">
          <label className=" control-label col-md-12" htmlFor="Description">
            Description
          </label>
          <div className="col-md-4">
            <input
              className="form-control"
              type="text"
              name="description"
              defaultValue={this.state.description}
              required
            />
          </div>
        </div>
        <div className="form-group">
          <button type="submit" className="btn btn-default">
            Save
          </button>
        </div>
      </form>
    )
  }

  handleSubmit(event) {
    event.preventDefault()
    const data = new FormData(event.target)

    // POST request for Add employee.
    fetch('https://localhost:44360/api/Articles/', {
      method: 'POST',
      body: data,
    })
      .then(response => response.json())
      .then(responseJson => {
        this.props.history.push('/articles')
      })
  }
}

Upvotes: 0

Views: 1025

Answers (1)

Alexis
Alexis

Reputation: 5831

You forgot to add the exact props on your <Route /> and the url /articles/create is matching both routes.

When exact={true} or exact is passed into <Route/> props the Route will be rendering the component if the location.pathname match exactly you path.

<Route exact={true} path='/articles' component={ArticlesIndex} />
<Route path='/articles/create' component={ArticlesCreate} />

Upvotes: 2

Related Questions