ReactJS does not re-loads my updated component when separate links are clicked from the navbar

152 Views Asked by At

I have this case where, in the nav bar I have different categories eg Science, business etc. When clicked on ay of these we are supposed to render News component with the appropriate category.

But in my case on clicking on any of the link, although the address bar did change but news component was not being reloaded with the new category that I provided.

Here are my files :

App.js

import './App.css';

import React, { Component } from 'react'
import NavBar from './components/NavBar';
import News from './components/News';
import {
  BrowserRouter as Router,
  Switch,
  Route
} from "react-router-dom";

export default class App extends Component {
  constructor() {
    super();
    this.category = 'about';
    this.uniqueKeyForRemountingNewsComponent = '';
  }
  render() {
    return (
      <Router>
        <div>
          <NavBar/>
          <Switch>
            <Route exact path="/about">
            </Route>
            <Route exact path="/business">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'business'}
            </Route>
            <Route exact path="/entertainment">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'entertainment'}
            </Route>
            <Route exact path="/general">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'general'}
            </Route>
            <Route exact exactpath="/health">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'health'}
            </Route>
            <Route exact path="/science">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'science'}
            </Route>
            <Route exact path="/sports">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'sports'}
            </Route>
            <Route exact path="/technology">
              {this.uniqueKeyForRemountingNewsComponent = this.category = 'technology'}
            </Route>
          </Switch>
          <News key={this.uniqueKeyForRemountingNewsComponent} pageSize={5} country="in" category={this.category}/>
        </div>
      </Router>
    )
  }
}

NavBar.js

import React, { Component } from 'react'
import {
    Link
  } from "react-router-dom";

export class NavBar extends Component {

    render() {
        return (
            <div>
                <nav className="navbar navbar-expand-lg navbar-dark bg-dark">
                    <div className="container-fluid">
                        <Link className="navbar-brand" to="/">NewsMonkey</Link>
                        <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                        </button>
                        <div className="collapse navbar-collapse" id="navbarSupportedContent">
                        <ul className="navbar-nav me-auto mb-2 mb-lg-0">
                            <li className="nav-item">
                            <Link className="nav-link active" aria-current="page" to="/">Home</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/business">Business</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/entertainment">Entertainment</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/general">General</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/health">Health</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/science">Science</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/sports">Sports</Link>
                            </li>
                            <li className="nav-item">
                            <Link className="nav-link" to="/technology">Technology</Link>
                            </li>

                        </ul>
                        </div>
                    </div>
                </nav>
            </div>
        )
    }
}

export default NavBar

News.js

import React, { Component } from 'react'
import NewsItem from './NewsItem'
import Spinner from './Spinner'
import PropTypes from 'prop-types'


export class News extends Component {

    static defaultProps = {
        country: 'in',
        pageSize: 5,
        category: 'general'
    }

    static propTypes = {
        country: PropTypes.string.isRequired,
        pageSize: PropTypes.number.isRequired,
        category: PropTypes.string.isRequired,
    }

    constructor(){
        super();
        this.state = {
            articles:[],
            loading:true,
            page:1,
            totalResults : 0
        }
    }

    async componentDidMount() {
        let url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=APIKEY&page=1&pageSize=${this.props.pageSize}`;
        this.setState({loading: true});
        let data = await fetch(url);
        console.log(data);
        let parsedData = await data.json();
        this.setState({articles: parsedData.articles, totalResults: parsedData.totalResults, loading: false})
        console.log(this.state);
    }

    handlePrevClick = async ()=>{
        let currentPage = this.state.page;
        let url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=APIKEY&page=${currentPage-1}&pageSize=${this.props.pageSize}`;
        this.setState({loading: true});
        let data = await fetch(url);
        console.log(data);
        let parsedData = await data.json();
        console.log(parsedData);
        if(parsedData)
            this.setState({articles: parsedData.articles, page : currentPage - 1, loading: false});
            console.log(this.state);
    }

    handleNextClick = async ()=>{
        let currentPage = this.state.page;
        let url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=APIKEY&page=${currentPage+1}&pageSize=${this.props.pageSize}`;
        this.setState({loading: true});
        let data = await fetch(url);
        console.log(data);
        let parsedData = await data.json();
        console.log(parsedData);
        if(parsedData)
            this.setState({articles: parsedData.articles, page : currentPage + 1, loading: false});
            console.log(this.state);
    }

    render() {
        return (
            <div className="container my-3">
                <h3 className="text-center">NewsMonkey - Top headlines</h3>
                {this.state.loading && <Spinner/>}
                
                <div className="row">
                {!this.state.loading && this.state.articles.map((element)=>{
                    return (
                    <div className="col-md-4" key={element.url}>
                        <NewsItem title={element && element.title?element.title.slice(0, 45): ""} description={element && element.description?element.description.slice(0, 50):""}
                        imageUrl={element.urlToImage}
                        newsUrl ={element.url}/>
                    </div>
                )})}
                </div>
                <div className="container d-flex justify-content-between">
                    
                    <button type="button" disabled={this.state.page<=1} className="btn btn-dark" onClick={this.handlePrevClick} >&larr; Prev</button>
                    <button type="button" disabled={this.props.pageSize*this.state.page>=this.state.totalResults} className="btn btn-dark" onClick={this.handleNextClick}>Next &rarr;</button>
                </div>
                
            </div>

        )
    }
}

export default News
1

There are 1 best solutions below

17
On

you shouldn't try to set a variable but just directly put the component

export default class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <NavBar/>
          <Switch>
            <Route exact path="/business">
              <News pageSize={5} country="in" category={'business'}/>
            </Route>
            <Route exact path="/entertainment">
              <News pageSize={5} country="in" category={'entertainment'}/>
            </Route>
          </Switch>
          
        </div>
      </Router>
    )
  }
}

every time you hit a /business it will render the component that is a child of <Route exact path="/business">

if you call /entertainment, it renders the child component you put under <Route exact path="/entertainment">

and so on

If you want to make that a bit more "clean", you could create a component that returns a Route


const RouteCategory = ({category}) => (
  <>
            <Route exact path={`${'/' + category `}>
              <News pageSize={5} country="in" category={category}/>
            </Route>
  </>
)

export default class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <NavBar/>
          <Switch>
            {['business', 'entertainment'].map(category => <RouteCategory category={category}>)}
          </Switch>

        </div>
      </Router>
    )
  }
}

I haven't tested this code but you get the idea