hi i'm doing a little social network with reactjs
i've created an api with express ans sequelize where i can find the users and the messages that users have done
i want that on the wall, users can like a message
the route is done in my backend and it work but i have an issu with my front
when i want to check the checkox of a message all of theme are checked
i don't understand why all of my checkBox checked at the same time and i don't see how can i check only one and let the other do there life
here is the code
import React, { useEffect, useState } from 'react'
import './Wall.scss'
import axios from 'axios'
import {Link} from "react-router-dom"
import jwt_decode from "jwt-decode"
const token = localStorage.getItem('token')
const config = {
headers: { authorization: `Bearer ${JSON.parse(token)}` }
}
const getAllMessage = 'http://localhost:3001/api/message/'
function Wall() {
const [message, setMessage] = useState([])
useEffect(() => {
axios.get(getAllMessage, config)
.then((res)=>{setMessage(res.data) ; console.log('useEffect')})
.catch((err) => { console.log(err)})
},[])
const [isChecked, setIsChecked] = useState()
const checkHandler = (e) =>{
let item = e.target.closest('[data-id]')
const disLikeMessage = `http://localhost:3001/api/like/dislike/${item.dataset.id}`
const likeMessage = `http://localhost:3001/api/like/${item.dataset.id}`
if(isChecked === item.checked){
console.log('unchecked')
axios.post(disLikeMessage,{}, config)
.then((res)=>{setIsChecked(!isChecked)})
.catch((err) => { console.log(err)})
} else{
console.log('checked')
axios.post(likeMessage,{}, config)
.then((res)=>{setIsChecked(!isChecked)})
.catch((err) => { console.log(err)})
}
}
return (
<div className='containerWall'>
<section className='cardWall'>
<ul className='cardUl'>
{message.map(item =>(
<li key = {item.id} >
<div className ='cardMessage'>
<Link to = {`/Wall/Message/${item.id}`}>
<img src = {item.picture} alt="" className='cardImage'/>
<div className='cardContent'>
<h2>{item.title}</h2>
<p>{item.content} </p>
<p>{item.createdAt}</p>
</div>
</Link>
<div className='likeSystem'>
<input type="checkbox" className='like'data-id={item.id checked={isChecked} onChange={checkHandler} />
<p>{item.likes}</p>
</div>
</div>
</li>
))}
</ul>
</section>
</div>
)
}
export default Wall
HAve any idea how to solve this?
(ps i'm a react beginner so be easy on me i want to understand :) ty for your time )
You have a single state for all the checkbox so they all share the same isChecked value.
If you want to keep one checked value in state per message, you could refactor it in its own component.
A better approach would be to not store any checked state and just rely on the item.checked attribute, when it's updated, you refetch the messages and update the state. React query allows to do this very easily.
const Like = ({ item }) => {
const [checked, setChecked] = useState(item.checked);
function checkHandler({target}) {
const url = `http://localhost:3001/api/like/${target.checked ? 'dislike/' + item.id : item.id}`
axios
.post(url, {}, config)
.then((res) => {
setChecked(prev => !prev);
})
.catch((err) => {
console.log(err);
});
}
return (
<div className="likeSystem">
<input
type="checkbox"
className="like"
checked={checked}
onChange={checkHandler}
/>
<p>{item.likes}</p>
</div>
);
};
Related
so i'm trying to implement a custom react hook for fetch. It's working fine, but i can't seem to do it with the errors. if i try to display the error in a custom component it's says object is not a valid React child ... okey i know that, but how then it's working when there's no error in the componenet ? Here's the code:
Hook:
import { useEffect, useState } from "react";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal: signal })
.then(res => res.json())
.then(result => {
setData([result]);
})
.catch(err => {
setError(true);
setErrorMessage(err);
})
return () => {
setError(false);
controller.abort();
};
}, [url]);
return { data, error, errorMessage };
};
export default useFetch;
Component:
const WeatherNow = () => {
const { city } = useContext(CityContext);
import ErrorHandler from '../error-component/ErrorHandler';
const { data, error, errorMessage } = useFetch(`https://api.weatherapi.com/v1/forecast.json?key=${process.env.REACT_APP_API_KEY}&q=${city}&aqi=no`);
if (error) {
return <>
<ErrorHandler props={errorMessage} />
</>
};
return (
<>
{data && data.map(x => (
<div className="today-forecast" key={city}>
<h4>
{city}
</h4>
<div>
{x.current.condition.text}
</div>
<div>
<img src={x.current.condition.icon} alt='' />
</div>
<h3>
{x.current.feelslike_c} *C
</h3>
<h5 className='rain-wind'>
Rain: {x.current.precip_in} % / Wind: {x.current.wind_kph} km/h
</h5>
<div className='links'>
<Link to='/hourly'>Hourly</Link> <Link to='/daily'>Daily</Link>
</div>
</div>
))}
</>
);
};
The ErrorHandler:
import './ErrorHandler.css';
import error from './error.png';
const ErrorHandler = ({ props }) => {
return (
<div className="error-component">
<div>
<h4>
{props}
</h4>
</div>
<div>
<img src={error} />
</div>
</div>
);
};
export default ErrorHandler;
Because of the catch (err) is an unknown type, it might return anything and more likely an object with a message key.
Try to change the way you are setting the error message and make sure it’s a string:
setErrorMessage(typeof err?.message === "string" ? err.message : "Unknown Error");
Warning
Using process.env.REACT_APP_API_KEY in client side is not safe at all.
The problem is in the catch block , somehow the err is inpromise and i can't use it
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 am trying to create a history page with react hooks that keeps track of the users most recent searches they don't have to be persistent through refreshes only from this session.
my search component looks like this this is a simple app that does not need a UI just a simple navigation on the search page it will show the results and on the history page I would like to be able to show the previous searches from this session
I am trying to keep track of the debouncedTerm so I can display it in a new component
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const Search = () => {
const history = [];
const [term, setTerm] = useState('');
const [debouncedTerm, setDebouncedTerm] = useState(term);
const [results, setResults] = useState([]);
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedTerm(term);
}, 1000);
return () => {
clearTimeout(timerId);
};
}, [term]);
useEffect(() => {
const search = async () => {
const { data } = await axios.get('http://hn.algolia.com/api/v1/search?', {
params: {
query: debouncedTerm,
},
});
setResults(data.hits);
};
if (debouncedTerm) {
search();
}
}, [debouncedTerm]);
const renderedResults = results.map((result) => {
return (
<div key={result.objectID} className="item">
<div className="right floated content">
<a className="ui button" href={result.url}>
Go
</a>
</div>
<div className="content">
<div className="header">{result.title}</div>
</div>
</div>
);
});
return (
<div>
<div className="ui form">
<div className="field">
<label>Hacker News Search:</label>
<input
value={term}
onChange={(e) => setTerm(e.target.value)}
className="input"
/>
</div>
</div>
<div className="ui celled list">{renderedResults}</div>
</div>
);
};
export default Search;
Your code looks like it's going in the right direction but you have a constant const history = []; and you must keep in mind that this will not work, because you will have a new constant re-declared in every render. You must use React setState like so:
const [history, setHistory] = useState([]);
You can read more about it in the React documentation.
edit:
In order to add new elements to the existing history you have to append it like this:
setHistory(oldHistory => [...oldHistory, newHistoryElement]);
After submitting with UpdateParams, the new url is called and a JSON object with the new queried data is returned as expected.
The form updates the two state vars.
However, the products in the all-products view are not updated to reflect the form input.
What do I need to do to to refresh the render to reflect the new data in product?
//all-products.js
import Link from 'next/link'
import React from 'react';
import { useState } from 'react';
//gets data from local api
async function getData(rank, keyword){
const res = await fetch(`http://localhost:4000/api?top=${rank}&keyword=${keyword}`);
return res;
}
export async function getStaticProps() {
const rank = 5;
const keyword = "shorts";
const response = await getData(rank, keyword);
const products = await response.json();
console.log(products);
if (!products) {
return {
notFound: true,
}
}
return {
props: {
products,
},
}
}
export default function AllProducts(stuff) {
let {products} = stuff;
const [rank, setRank] = useState("3");
const [keyword, setKeyword] = useState("shoes");
//from form
const updateParams = async (e) => {
e.preventDefault();
const response= await getData(rank, keyword);
products = await response.json();
}
return (
<div>
<input
type='text'
placeholder='topRank'
value={rank}
onChange={e => setRank(e.target.value)}
/>
<input
type="text"
placeholder='searchTerm'
value={keyword}
onChange={e => setKeyword(e.target.value)}
/>
<button
type='submit'
onClick={updateParams}>
Update Params</button>
<ul>
{products.Products.map((product) => {
return (
<div key={product.Id}>
<li>{product.Name}</li>
<li><img width={300} src={ product.imgUrl } alt="product image" /></li>
</div>
) }
)}
</ul>
</div>
)
}
getStaticProps is run at build-time so it'll provide the data that's available at that time. To update the UI after the user interacts with the form you should put products into state and update it once new params are submitted and you retrieve the new products.
// all-products.js - removed irrelevant code for simplicity
export default function AllProducts(stuff) {
const [products, setProducts] = useState(stuff.products);
//...
const updateParams = async (e) => {
e.preventDefault();
const response = await getData(rank, keyword);
const newProducts = await response.json();
setProducts(newProducts);
}
return (
//...
<ul>
{products.Products.map((product) => {
return (
<div key={product.Id}>
<li>{product.Name}</li>
<li><img width={300} src={product.imgUrl} alt="product image" /></li>
</div>
)
})}
</ul>
//...
)
}
and thank you in advance for any help.
I am trying to build a web app that fetches data from an API, in this case a movie database API, but when i am trying to map all the movies from a specific title search i get the .map is not a function error, what i am doing wrong ? Can't i use useState to display the data ?
When i do console.log (search) i can see the array with all the data :
import React, {useEffect, useState} from 'react';
import axios from 'axios';
export default function RandomFacts() {
const [input, setInput] = useState('');
const [search, setSearch] = useState(['']);
useEffect(() => {
apiCall();
}, [input]);
const moviesList = search && search.map((movie, index) =>
<div className="movies" key="index">
<li><h2>{movie.Title}</h2></li>
<li><img src={movie.Poster} alt="poster" /></li>
</div>,
);
const apiCall = async () => {
const url = 'http://www.omdbapi.com/?s='+input+'&page=1&apikey=536a34c3';
try {
const response = await axios.get(url);
if (response.status === 200 && response !== undefined) {
const data = response.data;
setSearch(data.Search);
console.log(search);
}
} catch (error) {
console.log(error);
}
};
return (
<div className="main">
<h1>Movies</h1>
<div className="textInput">
<form>
<label>
<input type="text" value={input}
onChange={(e) => setInput(e.target.value)}
/>
</label>
</form>
</div>
<div className="movies">
{moviesList}
</div>
</div>
);
}
The API is returning a response Object with a data key containing the keys Search, TotalResults, and Response. You're trying to map this response Object instead of the Array contained in response.data.Search.
So you should be using setSearch(response.data.Search).