Search code examples
javascriptreactjsreact-router-dom

Using react-router-dom v6.8.2 in react class based component Link doesn't working


I am learning react js and creating a news app using newsapi in a React class component. I have installed react-router-dom@6.8.2 in my appilcation. I don't know what I did wrong but in my application Link is not working and my url did change but I don't not get desire output according to category.

file structure:

enter image description here

Here is my index.js file

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

file : App.js

import NavBar from "./components/NavBar";
import News from "./components/News";
import { Route, Routes } from "react-router-dom";

export default class App extends Component {
  render() {
    return (
      <div>
        <NavBar />
        <Routes>
          <Route
            path="/"
            element={<News pageSize={5} country="in" category="general" />}
          />
          <Route
            path="/business"
            element={<News pageSize={5} country="in" category="business" />}
          />
          <Route
            path="/entertainment"
            element={<News pageSize={5} country="in" category="entertainment" />}
          />
          <Route
            path="/health"
            element={<News pageSize={5} country="in" category="health" />}
          />
          <Route
            path="/science"
            element={<News pageSize={5} country="in" category="science" />}
          />
          <Route
            path="/sports"
            element={<News pageSize={5} country="in" category="sports" />}
          />
          <Route
            path="/technology"
            element={<News pageSize={5} country="in" category="technology" />}
          />
        </Routes>
      </div>
    );
  }
}

file: NavBar.js

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

export default class NavBar extends Component {
  render() {
    return (
      <div>
        <nav
          className="navbar navbar-expand-lg bg-body-tertiary"
          data-bs-theme="light"
        >
          <div className="container-fluid">
            <img src="WHHlogo.png" className="Web-logo" alt="logo" />
            <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 mx-3">
                <li className="nav-item">
                  <Link className="nav-link" 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="/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 className="d-flex form-check form-switch">
                <input
                  className="form-check-input"
                  type="checkbox"
                  role="switch"
                  id="flexSwitchCheckDefault"
                />
                <label
                  className="form-check-label" 
                  htmlFor="flexSwitchCheckDefault"
                >
                  Dark Mode
                </label>
              </div>
            </div>
          </div>
        </nav>
      </div>
    )
  }
}

file: News.js

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


export default class News extends Component {
  static defaultProps = {
    country:'in',
    pageSize:8,
    category:'general'
  }
  static propTypes = {
    country: PropTypes.string,
    pageSize:PropTypes.number,
    category:PropTypes.string,
  }
  constructor() {
    super();
    this.state = {
      articles: [],
      loading: false,
      page: 1,
    }
  }
  async componentDidMount() {
    let url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=**writeyourapikey**&page=1&pageSize=${this.props.pageSize}`;
    this.setState({
      loading:true
    });
    let data = await fetch(url);
    let parseData = await data.json();
    this.setState({ articles: parseData.articles })
    console.log(parseData);
    this.setState({
      articles: parseData.articles,
      totalResults: parseData.totalResults,
      loading:false
    })
  }
  handleNextClick = async () => {
    console.log("next")
    let url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&apiKey=**writeyourapikey**&page=${this.state.page + 1}&pageSize=${this.props.pageSize}`;
    this.setState({
      loading:true
    });
    let data = await fetch(url);
    let parseData = await data.json();
    console.log(parseData);

    this.setState({
      page: this.state.page + 1,
      articles: parseData.articles,
      loading:false
    })
  }
  handlePreClick = async () => {
    console.log("previous")

    let url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&apiKey=**writeyourapikey**&page=${this.state.page - 1}&pageSize=${this.props.pageSize}`;
    this.setState({
      loading:true
    });
    let data = await fetch(url);
    let parseData = await data.json();
    console.log(parseData);

    this.setState({
      page: this.state.page - 1,
      articles: parseData.articles,
      loading:false
    })
  }
  render() {
    return (
      <div className='container my-3'>
        <h2 className="text-center">HeadlineHunter - Top Breaking News</h2>
        {this.state.loading && <Spinner/>}
        <div className="row">
          {!this.state.loading && this.state.articles.map((element) => (
            <div className="col-md-4" key={element.url}>
              <NewsItem title={element.title ? element.title : ""} description={element.description ? element.description : element.title} imgUrl={element.urlToImage} newsUrl={element.url} />
            </div>
          ))}
        </div>

        <div className="container d-flex justify-content-between ">
          <button disabled={this.state.page <= 1} className="btn btn-dark" onClick={this.handlePreClick} type="button">&larr; Previous</button>
          <button disabled={this.state.articles.length ===0} className="btn btn-dark" onClick={this.handleNextClick} type="button">Next &rarr;</button>
        </div>

      </div>
    )
  }
}


I am expecting to get the articles according to category wise


Solution

  • react-router-dom optimizes the rendered route components so they remain mounted when only the path they are rendered on updates. This means you should also implement the componentDidUpdate lifecycle method to handle when props update.

    I suggest abstracting the fetching logic from componentDidMount into a utility function that is called in both lifecycle methods. Then the category or country props update/change then refetch the appropriate data.

    Example:

    export default class News extends Component {
      ...
    
      fetchData = async () => {
        try {
          const url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=**witeyourapikey**&page=1&pageSize=${this.props.pageSize}`;
    
          this.setState({ loading: true });
    
          const data = await fetch(url);
          const parseData = await data.json();
        
          console.log(parseData);
          this.setState({
            articles: parseData.articles,
            totalResults: parseData.totalResults
          });
        } catch {
          // catch/handle any errors
        } finally {
          this.setState({ loading: false });
        }
      }
      
      componentDidMount() {
        this.fetchData();
      }
    
      componentDidUpdate(prevProps) {
        if (
          prevProps.country !== this.props.country ||
          prevProps.category !== this.props.category ||
          prevProps.pageSize !== this.props.pageSize
        ) {
          this.fetchData();
        }
      }
    
      ...
    
      render() {
        ...
      }
    }