I have written a project which receives data through an api. Clicking on each button displays corresponding news. For example, when you press the sports button, sports news comes. However, I want the All category to be active when the page is first opened. In other words, those news should have arrived without pressing the all button. How can I do this?
Not - The function inside useffect returns every time it renders and it doesn't work for me. For example, when you refresh the page while reading sports news, all news comes
import React, { useEffect, useState } from "react";
import SpinnerLoad from './components/SpinnerLoad'
import NewsItem from "./components/NewsItem";
import Category from "./components/data/Category"
const App = () => {
const [state, setState] = useState([]);
const [loading, setLoading] = useState(false)
const fetchValue = (category) => {
fetch(`https://inshorts-api.herokuapp.com/news?category=${category}`)
.then(res => res.json())
.then(res => {
setState(res.data)
setLoading(true)
})
.catch((error) => console.log(error))
setLoading(false);
};
const CategoryButton = ({ category }) => (
<button onClick={() => fetchValue(category)} style={{ textTransform: 'capitalize' }}>{category}</button>
);
useEffect(() => {
fetchValue('all')
}, [])
return (
<>
<div className="header-bg">
<h1 className="mb-3">News</h1>
<div className="btns ">
{Category.map((value, index) => {
return <CategoryButton category={value} key={index} />;
})}
</div>
</div>
<div className="news">
<div className="container">
<div className="row">
{
!loading
? <SpinnerLoad/>
:
state.map((data,index) => {
return (
<NewsItem
imageUrl={data.imageUrl}
author={data.author}
title={data.title}
content={data.content}
date={data.date}
key={data.id}
/>
);
})
}
</div>
</div>
</div>
</>
);
};
export default App;
import React from 'react'
import clock from "../components/assets/img/Clock.svg"
import user from "../components/assets/img/User.svg"
const NewsItem = (props) => {
const {imageUrl, title, content, date, author} = props
return (
<div class="col-lg-4 col-md-6 col-12 p-2">
<div className="newsItem">
<img src={imageUrl} alt=''/>
<div className="itemBody">
<p className='title'>{title}</p>
<div className="line"></div>
<p className='content'>{content}</p>
<div className="itemfooter">
<h6><img src={clock} alt='clock'/>{date}</h6>
<h6><img src={user} alt='user'/>{author}</h6>
</div>
</div>
</div>
</div>
)
}
export default NewsItem
In react, if you refresh the app , the state values will reinitialise.
From your question , it seems like you want to store the category value and even after refresh , you want to persist the category value..
For that you can store category value in local or sessionStorage..
const fetchValue = (category) => {
localStorage.setItem("category", category);
// your code
}
// in useEffect , you can check for the category value in the local Storage
useEffect(() => {
// check value in localStorage, if does not exist use "all" as default value
let categoryValue = localStorage.getItem("category") || "all" ;
fetchValue(categoryValue)
},[]);
Related
What I want is when I click on:
let Afeef = `/${category}`
<Link to={Afeef} className='products-categories'> <h4>{category}</h4></Link>
It should change products according to URL which could be "/electronics","/jewelry" etc but the problem I am facing is that it is changing my URL but the products are not changing. I can't understand what is the problem here. I tried different things but I cant understand it.
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom';
import './Allproducts.css'
import Categories from './categories.json'
import ForYouItem from './ForYouItem'
export default function Allproducts(props) {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch(`https://fakestoreapi.com/products/category/${props.category}`)
.then((res) => res.json())
.then((data) => setProducts(data))
}, [])
const [categories, setCategories] = useState([])
const updateCategory = async ()=> {
const url = "./categories.json"
let data = await fetch(url);
let parsedData = await data.json()
setCategories(parsedData.title)
}
useEffect(() => {
updateCategory();
}, [])
return (
<>
<div className="banner">
<h1>Afeef</h1>
<h4>Get best items in budget</h4>
</div>
<div className="main-grid">
<div className="left-grid">
<div className="left-categories">
<h1>Collections</h1>
{categories.map((category) => {
let Afeef = `/${category}`
return (
<Link to={Afeef} className='products-categories'> <h4>{category}</h4></Link>
)
}
)}
</div>
</div>
<div className="right-grid">
<div className="row ">
{products.map((product) => {
return (
<div className="col-md-4 my-2 Products-1">
<ForYouItem Title={product.title.slice(0, 50)} Price={product.price} Imageurl={product.image} rate={product.rating.rate} count={product.rating.count} />
</div>
)
}
)}
</div>
</div>
</div>
</>
)
}
im gonna try to explain what i understand from your code. So based on the flow of your code, the product can only be fetch once when the page loaded.
but i think in your useEffect that fetch product, you can add the state of Categories in the bracket "[categories]".
then add an onclick setstate product in your link.
so when you click your link, the categories state is updated. then because the bracket inside useEffect that have [categories] is updated the useEffect is run. hence fething new product.
I want to fetch the data when the button is clicked but the Newsitem component is running first and then updating the value of data_grabber. That means it is displaying the defalut values rather than the data that I fetched from the newsapi. After displaying the newsitem component with default values, data_grabber is updating the fetched data.
What can be the solution?
App.js
function App() {
const [input_data, setInput_data] = useState("");
const [btn_data, setBtn_data] = useState("");
const [data_grabber, setData_grabber] = useState([]);
return (
<>
<Navbar
input_data={input_data}
setInput_data={setInput_data}
setBtn_data={setBtn_data}
btn_data={btn_data}
data_grabber={data_grabber}
setData_grabber={setData_grabber}
/>
{data_grabber? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
</>
);
}
export default App;
Navbar.js
import { useEffect } from "react";
export default function Navbar(props) {
const onClicker = async (e) => {
e.preventDefault();
props.setBtn_data(props.input_data);
};
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=API_KEY`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
props.setData_grabber(data.articles)
};
fetcher();
}, [props.btn_data]);
return (
<div>
<form className="d-flex">
<input
onChange={(e) => props.setInput_data(e.target.value)}
value={props.input_data}
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button
className="btn btn-outline-success"
type="submit"
onClick={onClicker}
>
Search
</button>
</form>
</div>
NewsItem.js
import React, { Component } from "react";
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.data
const defaultImage = `https://blogger.googleusercontent.com/img/a/AVvXsEh20SgNNsDlKyWWmB7XgB5SfFY10M6CqJAq93HwGtssTn2cWz6w9zHPjXf91WwoWr27QeaC4HsGv2NxPOXUdvk6xodUojnw8rUuAkEMY3Qb4ucoVpN3nSyF8JW_xVDWa2aSMEWH387hPsfouSJyClLNburIcDbXIeJamuTHwiSvw4hdNnqeeICcvg1wrQ=w1200-h630-p-k-no-nu`
return (
<div>
<div className="card">
<img src={urlToImage?urlToImage:defaultImage} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{title?title:'No title available'}</h5>
<p className="card-text">
{description?description.slice(0, 50):"no description available"}...
</p>
<a href={url} target="_blank" rel="noreferrer"className="btn btn-primary">
read more
</a>
</div>
</div>
</div>
);
}
}
One fix could be to
make a variable of the updated state:
in the UseEffect ,
add :
const updated = data.articles
props.setData_grabber(updated)
Check whether data_grabber array is empty or not and then do the rendering inside App component as follows.
{
data_grabber.length > 0 ? (
data_grabber.map((news_data) => {
return <NewsItem news_data={news_data} />;
})
) : (
<div>No data available</div>
);
}
{ data_grabber !== undefined && data_grabber.length > 0 ? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
Check data_grabber is undefined or empty.
Then, fix NewsItem props.data like this.
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.news_data
also fix here in useEffect
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=c990aa0235da4635997afd1f7459860c`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
if(data.articles){
props.setData_grabber(data.articles)
}
};
fetcher();
I'm trying to present the movie API array data in bootstrap cards but it seems that I still don't understand something about react hooks or map functions because I can't get the code right. I get this "TypeError: setTrendingResults.map is not a function" .
My code:
import Hero from "./Hero";
import Footer from "./Footer";
import { Link } from "react-router-dom";
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
const TrendingCard = ({ trending }) => {
const trendPosterUrl = `https://image.tmdb.org/t/p/w500${trending.poster_path}`;
const trendDetailUrl = `/movies/${trending.id}`;
return (
<div className="col-lg-4 col-md-3 col-2">
<div className="card">
<img
src={trendPosterUrl}
class="card-img-top"
alt={trending.original_title}
/>
<div className="card-body">
<h5 className="card-title">{trending.original_title}</h5>
<Link to={trendDetailUrl} class="btn btn-primary">
Show details
</Link>
</div>
</div>
</div>
);
};
const TrendingView = (data) => {
const [trendingResults, setTrendingResults] = useState([]);
useEffect(() => {
fetch(
"https://api.themoviedb.org/3/trending/movie/week?api_key=776d38251dae661e04c01631cfa95286"
)
.then((response) => response.json())
.then((data) => {
setTrendingResults(data.results);
});
});
const trendingHtml = setTrendingResults.map((obj, i) => {
return <TrendingCard trending={obj} key={i} />;
});
return (
<div>
<Hero text="Trending" />
{trendingHtml && (
<div className="container">
<div className="row">{trendingHtml}</div>
</div>
)}
</div>
);
};
export default TrendingView;
Use valid state for mapping a card. trendingResults insted of setTrendingResults.
Here I convert a trendingHtml with React.useMemo which helps to avoid re-render-related stuff.
const TrendingView = (data) => {
const [trendingResults, setTrendingResults] = useState([]);
useEffect(() => {
fetch(
"https://api.themoviedb.org/3/trending/movie/week?api_key=776d38251dae661e04c01631cfa95286"
)
.then((response) => response.json())
.then((data) => {
setTrendingResults(data.results);
});
});
const trendingHtml = React.useMemo(()=> trendingResults?.map((obj, i) => {
return (<TrendingCard trending={obj} key={i} />)
}),[trendingResults])
return (
<div>
<Hero text="Trending" />
{trendingHtml && (
<div className="container">
<div className="row">{trendingHtml}</div>
</div>
)}
</div>
);
};
export default TrendingView;
setTrendingResults is used to set the value to trendingResults state. You have to use trendingResults instead of setTrendingResults.
const trendingHtml = trendingResults.map((obj, i) => {
return <TrendingCard trending={obj} key={i} />
})
How can I add a search function the data below (Ideally in another component, Nav Component)? I have tried passing the data via props and context and simply can not get it to work. I need to be able to filter through the data by Album name, Artist or potentially another attribute.
import React, { Component, useEffect, useState} from 'react'
import axios from 'axios';
import '../components/data.css';
function Data() {
const [Music, setMusic] = useState([], {});
useEffect(() => {
axios.get('https://itunes.apple.com/us/rss/topalbums/limit=100/json')
.then((data) => {
setMusic(data.data.feed.entry);
})
.catch((err) => console.log(err));
},[]);
return (
<div>
<article className="albums">
<ul className="album-items">
{Music.map(Music => {
const { id, name, label, title, artist, entry } = Music;
return (
<div className="album" >
<li className='album-list' key={id.attributes['im:id']}>
<h3 className='title'>Album Name: {Music['im:name'].label}</h3>
<img src={Music['im:image'][2].label} alt={Music['im:name'].label}/>
<h4 className='title'>Artist: {Music['im:artist'].label}</h4>
</li>
</div>
);
})}
</ul>
</article>
</div>
)
}
export default Data
Here is how you can search the album data based on album name and artist name.
Working App: Stackblitz
import React, { Component, useEffect, useState } from "react";
import axios from "axios";
function Data() {
const [Music, setMusic] = useState([], {});
const [search, setSearch] = useState("");
useEffect(() => {
axios
.get("https://itunes.apple.com/us/rss/topalbums/limit=100/json")
.then(data => {
setMusic(data.data.feed.entry);
})
.catch(err => console.log(err));
}, []);
//======================================
const handleSearch = event => {
console.log(event.target.value);
setSearch(event.target.value.toLowerCase());
};
const filtered = Music.length
? Music.filter(
music =>
music["im:name"]["label"].toLowerCase().includes(search) ||
music["im:artist"]["label"].toLowerCase().includes(search)
)
: [];
//======================================
return (
<div>
<input onChange={handleSearch} placeHolder={"search title, artist"} />
<article className="albums">
<ul className="album-items">
{filtered.map(Music => {
const { id, name, label, title, artist, entry } = Music;
return (
<div className="album">
<li className="album-list" key={id.attributes["im:id"]}>
<h3 className="title">
Album Name: {Music["im:name"].label}
</h3>
<img
src={Music["im:image"][2].label}
alt={Music["im:name"].label}
/>
<h4 className="title">Artist: {Music["im:artist"].label}</h4>
</li>
</div>
);
})}
</ul>
</article>
</div>
);
}
export default Data;
I'm trying to simply pass the Id of a clicked item to display on another page under a different component ("Cart") . At the bottom of the code below, I have a button containing <Cart test={product.id} /> which extracts the Id that I want to be displayed in "Cart" when the button is clicked.
However, I am instead getting an error message of:
Objects are not valid as a React child (found: object with keys
{history, location, match, staticContext}). If you meant to render a
collection of children, use an array instead.
Is there a simple syntax error?
import React, { useState, useEffect, Cart } from 'react';
import './../App.css';
import * as ReactBootStrap from 'react-bootstrap';
function Item(props) {
const [product, setProduct] = useState([]);
const [loading, setLoading] = useState(false);
const [quantity, setQuantity] = useState(1);
const [cost, setCost] = useState([]);
useEffect(async () => {
fetchItems();
}, []);
const itemId = props.match.params.item;
const fetchItems = async () => {
const data = await fetch('https://fakestoreapi.com/products/' + itemId);
const items = await data.json();
setProduct(items);
setLoading(true);
setCost(items.price);
};
function priceUSD(change) {
return change.toFixed(2);
}
useEffect(() => {
const newCost = quantity * product.price;
setCost(priceUSD(newCost));
}, [quantity]);
return (
<div className="App">
<h2>Item</h2>
<div className="gridContainer">
{loading ? (
<div key={itemId} className="productStyle">
<img src={product.image} className="productImage"></img>
<p>{product.title}</p>
<p>{product.description}}</p>
<p>${priceUSD(product.price)}</p>
<div className="quantity">
<button
className="btn minus-btn"
type="button"
onClick={quantity > 1 ? () => setQuantity(quantity - 1) : null}
>
-
</button>
<input type="text" id="quantity" value={quantity} />
<button className="btn plus-btn" type="button" onClick={() => setQuantity(quantity + 1)}>
+
</button>
</div>
<button type="button" onClick={() => <Cart test={product.id} />}>
Add to shopping cart ${cost}
</button>
</div>
) : (
<ReactBootStrap.Spinner className="spinner" animation="border" />
)}
</div>
</div>
);
}
export default Item;
Cart
import React, { useState, Item } from 'react';
import './../App.css';
import './Item.js';
function Cart(test) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
export default Cart;
Component props are objects. You can read more about them in the official documentation.
You can either destructure the props of the Cart component:
function Cart({test}) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
or use explicitly test property of props:
function Cart(props) {
return (
<div className="App">
<p>{props.test}</p>
</div>
);
}