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:
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">← Previous</button>
<button disabled={this.state.articles.length ===0} className="btn btn-dark" onClick={this.handleNextClick} type="button">Next →</button>
</div>
</div>
)
}
}
I am expecting to get the articles according to category wise
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() {
...
}
}