I'm showing array of objects that is received from api call.I'm using react hooks.when I tried to iterate through array of objects it return foreach is not a function.
for this can I use await and async.
function searchArticle() {
const [articleList, setArticleList] = useState([]);
const { articleApiStatus, articleCurrentPage, searcArticle } = state;
useEffect(() => {
setArticleList(state.articleList);
articleListApiCall(articleCurrentPage);
}, [])
const articleListApiCall = (page = 1) => {
const isNewPage = articleCurrentPage !== page;
if (!articleApiStatus || isNewPage) {
getArticleList(page, '')
}
}
const getArticleList = async (page, searchkey) => {
const requestBody = {
page: page - 1, searchkey
}
await onArticleSearchPost(requestBody).then(articleresult => {
setArticleList(articleresult);
}).catch(err => {
console.log(err);
})
}
return (
<React.Fragment>
<div className="article-section">
<div className="search-result">
<Collapse >
{
articleList.forEach(element => {
<Panel header={element.article_title} key={element._id}>
<p>{element.article_body}</p>
</Panel>
})
}
</div>
<div className="pagination-section">
<Pagination defaultCurrent={1} total={50} />
</div>
</div>
</React.Fragment>
)
}
export default searchArticle;
Edit : I'm receiving following data from api call
[
{
"_id":"5d9efbc3b29b2876700adf6f",
"postedBy":"Admin",
"datePosted":"1570700227888",
"__v":0
}
]
First of all, do not use forEach to render JSX, it won't work. Use map instead:
<Collapse>
{articleList.map(element => { // use map
return (
<Panel header={element.article_title} key={element._id}>
<p>{element.article_body}</p>
</Panel>
);
})}
</Collapse>
Second, make sure that state.articleList, which you're setting to state in useEffect, is an actual array.
Related
I have a string array that contains usernames. I want to fetch those users in a loop, put them in an array and render a component for each user. I can retrieve the data from API and print it on console but the code below gives me a white screen.
Here I'm trying to fetch data one by one using fav_list array than contains usernames. Then I want to send the data to another component called InfluencerFavoritesCard and render them. Where I'm doing wrong?
import React from 'react'
import InfluencerFavoritesCard from '../components/influencerFavoritesCard/InfluencerFavoritesCard';
import "./indexPages.css"
import "./favorites.css"
const fav_list = ["cagritaner", "acunilicali", "neslihanatagul"];
async function ListFavorites() {
let array = new Array;
var fetches = [];
for (let i = 0; i < fav_list.length; i++) {
console.log(fav_list[i]);
let uname = fav_list[i];
fetches.push(
fetch(`http://localhost:3001/api/users/${uname}`)
.then(res => {return res.text(); })
.then(res => {
array.push(res);
console.log(res);
}
)
);
}
Promise.all(fetches).then(function () {
console.log(fetches);
console.log(array[0]);
console.log(array.length);
});
return (
<div>
{array.map(item => (< InfluencerFavoritesCard infCard = { item } /> ))}
</div>
);
}
function Favorites() {
return (
<div className='favoritesMain'>
<div className='favoritesTitle-div'>
<h3 className="favoritesTitle">FAVORİLER</h3>
</div>
<div className='favoritesContainer-wrapper'>
<div className='favoritesContainer'>
{<ListFavorites/>}
</div>
</div>
<div className='favoritesFooter'></div>
</div>
);
}
export default Favorites
InfluencerFavoritesCard.jsx
import React from 'react';
import './influencerFavoritesCard.css';
const InfluencerFavoritesCard = ({ infCard }) => {
return (
<div className='infCard'>
<div className='infCard-text-info' >
<div className='infCard-name'>
<h3>{infCard.name}</h3>
</div>
<div className='infCard-username'>
<h4>{infCard.username}</h4>
</div>
<div className='infCard-categories'>
<h4>{infCard.categories}</h4>
</div>
</div>
</div>
)
}
export default InfluencerFavoritesCard;
================================
UPDATED:
I have updated the parent component like below.
export function Favorites() {
const [users, setUsers] = useState([]);
const fav_list = ["cagritaner", "acunilicali", "neslihanatagul"];
useEffect(() => {
const tempUsersCollection = [];
fav_list.map((x, i) => {
fetch(`http://localhost:3001/api/users/${x}`)
.then(res => {return res.json(); })
.then(res => {
tempUsersCollection.push(res.data.tour);
console.log(res);
}
);
});
console.log(tempUsersCollection);
setUsers(tempUsersCollection);
}, []);
return (
<div className='favoritesMain'>
<div className='favoritesTitle-div'>
<h3 className="favoritesTitle">FAVORİLER</h3>
</div>
<div className='favoritesContainer-wrapper'>
<div className='favoritesContainer'>
{users.map((item, index) => (
<InfluencerFavoritesCard
infCard={item}
key={`influencer-${item.username}-${index}`}
/>
))}
</div>
</div>
<div className='favoritesFooter'></div>
</div>
);
}
output of the console.log(tempUsersCollection) (AFTER res.json() !! )
Array []
0: Object { username: "neslihanatagul", biography: "contact#neslihanatagul.com", profile_picture_url: "https://scontent.fist4-1.fna.fbcdn.net/v/t51.2885-15/5787050…0_AfDfN8wtsD18vya_aLLw6M4UpP8Xx16jb9b4Hsh6cJ3wjA&oe=63A713AF", … }
1: Object { username: "cagritaner", biography: "Hüzünlü Bir Ponçik ve Erkeklerin İç Sesi kitaplarının yazarı.\niş birlikleri için; #goygoynetworkinfo", profile_picture_url: "https://scontent.fist4-1.fna.fbcdn.net/v/t51.2885-15/4424442…0_AfCXmyBbUrKQjpzsGEHNEMHXoNP7HZ8UKaYXHAL4S0DFlA&oe=63A6926B", … }
2: Object { username: "acunilicali", biography: "Acun Ilıcalı Resmi Instagram Hesabıdır.", profile_picture_url: "https://scontent.fist4-1.fna.fbcdn.net/v/t51.2885-15/1193932…0_AfDuL0toVKsSGOrnXWISBMAR78G79QwNfxJmm5cNFBuW2A&oe=63A78969", … }
length: 3
<prototype>: Array []
Favorites.jsx:136
The thing is that return statement is being rendered before all the promises are resolved, meaning that it's empty. This is exactly how it should work, so no bug here.
What you need to do is as other mentioned, use useState and useEffect to control the data:
// This will hold your collection
const [users, setUsers] = useState([])
...
// And here you need to update that collection
useEffect(()=>{
const temp = []
fetch(`http://localhost:3001/api/users/${uname}`)
.then(res => {return res.text(); })
.then(res => {
temp.push(res);
}
)
setUsers(temp)
}, [])
Later on the return you can do this:
// This controls if there are no users
if(users.length <= 0){
return <>There are no users</>
}
return (
<div className='favoritesMain'>
<div className='favoritesTitle-div'>
<h3 className="favoritesTitle">FAVORİLER</h3>
</div>
<div className='favoritesContainer-wrapper'>
<div className='favoritesContainer'>
{users.map(item => (<InfluencerFavoritesCard infCard = { item } /> ))}
</div>
</div>
<div className='favoritesFooter'></div>
</div>
);
Created a CodeSandbox so you can see this working
UPDATE:
// Call the function after the first render
useEffect(() => {
fetchUsers();
}, []);
// Wrapped all the calls in a Promise.all and update the state
async function fetchUsers() {
const response = await Promise.all(
fav_list.map((x) =>
fetch(`https://pokeapi.co/api/v2/pokemon/${x}`)
.then((res) => res.json())
.then((user) => user)
)
);
console.log(response);
setUsers(response);
}
// Handle the case where nothing is retrieved
if (users.length <= 0) {
return <>There are no users</>;
}
To fetch data asynchronously, you need to use the useEffect hook. You should store the data using a useState hook and then set that data when you get a response from your fetch request.
I wanna make follow/unfollow toggle button, and following / follower list(object in array) will be called seperately from server.
Follower list needs to have both unfollow/follow button status.
When I call follower list, how can I check the IDs of the people who follow me matches the IDs of my following list & reflect in on the button?
example following, follower object in array
[{id: 1, profileImg: xxx},{id: 2, profileImg: xxx},{id: 3, profileImg: xxx}... ]
my code in js below
const { select } = props;
const [choice, setChoice] = useState(select);
const [followingList, setFollowingList] = useState([]);
const [followerList, setFollowerList] = useState([]);
const handleChoice = (e) => {
setChoice(e.target.value);
};
useEffect(() => {
getFollowing()
.then((res) => {
setFollowingList(res);
})
.then(
getFollower().then((res) => {
setFollowerList(res);
}),
);
}, []);
my code in html
<Container onClick={(e) => e.stopPropagation()}>
<TogglebtnContainer>
<ToggleBtn onClick={handleChoice} value="following" choice{choice}>Following</ToggleBtn>
<ToggleBtn onClick={handleChoice} value="follower" choice={choice}>Follower</ToggleBtn>
</TogglebtnContainer>
<FollowContainer>
<Follow>
{choice === 'following'? (followingList.map((follow, idx) => {
return (
<div className="follow-item" key={idx}>
<div className="follow-img"><img src={follow.profileImg} alt="UserPic" /> </div>
<div className="follow-name">{follow.nickname}</div>
<FollowBtn key={follow.id}>Unfollow</FollowBtn></div>
);})
: (followerList.map((follow, idx) => {
return (
<div className="follow-item" key={idx}>
<div className="follow-img">
<img src={follow.profileImg} alt="UserPic" />
</div>
<div className="follow-name">{follow.nickname}</div>
<FollowBtn key={follow.id}>follow</FollowBtn>
</div>
})}
</Follow>
</FollowContainer>
</Container>
I thought I could check if this IDs matches IDs of my following list and create a new boolean state.
(ex [isFollowing, setIsFollowing = useState(false)) but couldn't find a way.
getFollower().then((res) => {
setFollowerList(res);
To know which followers the user is already following and follow/unfollow followers
short answer, set a flag when loading the data
useEffect(() => {
let isValidScope = true;
const fetchData = async () => {
const followingList = await getFollowing();
if (!isValidScope) { return; }
setFollowingList(followingList);
let followersList = await getFollower();
if (!isValidScope) { return; }
const followingUserIds = followingList?.map(f => f.id)
followersList = followersList?.map(follower => {
return followingUserIds?.includes(follower.id) ?
{ ...follower, isFollowing: true } : follower
}
setFollowerList(followersList)
}
fetchData()
return () => { isValidScope = false }
}, []);
const onFollowFollower = (followerId) => {
const followersList = followerList?.map(follower => {
return follower.id === followerId ?
{ ...follower, isFollowing: true } : follower
}
setFollowerList(followersList)
}
const onUnfollowFollower = (followerId) => {
const followersList = followerList?.map(follower => {
return follower.id === followerId ?
{ ...follower, isFollowing: false } : follower
}
setFollowerList(followersList)
}
Render code
<Follow>
{choice === 'following'? (followingList.map((follow, idx) => {
return (
<div className="follow-item" key={idx}>
<div className="follow-img"><img src={follow.profileImg} alt="UserPic" /> </div>
<div className="follow-name">{follow.nickname}</div>
<FollowBtn key={follow.id}>Unfollow</FollowBtn>
</div>
);})
: (followerList.map((follow, idx) => {
return (
<div className="follow-item" key={idx}>
<div className="follow-img">
<img src={follow.profileImg} alt="UserPic" />
</div>
<div className="follow-name">{follow.nickname}</div>
{ follow?.isFollowing ? <FollowBtn () => onUnfollowFollower(follow.id)>Unfollow</FollowBtn> : null }
{ !follow?.isFollowing ? <FollowBtn onClick={() => onFollowFollower(follow.id)>Follow</FollowBtn> : null }
</div>
})}
</Follow>
You can read about working with list in the new React docs
if you are refetching the follower and following list on every change it will be better to recalculate the followers list using a useMemo on every change
Hope this helps you in someway
I am making a mern ecommerce website i just want to see how useEffect works so i console.log in some part of useEffect and loadFilteredResults i saw that --->
initial
entry
skip
entry1
screen shot
but i think it shoud be-->
initial
entry
entry1
skip
why console give this?? i am a begginer, i am a self learner , so please if you need any extra info please comment.
code snippet-->
const loadFilteredResults = (newFilters) => {
console.log("entry")
getFilteredProducts(skip, limit, newFilters).then((data) => {
console.log("entry1")
if (data.error) {
setError(data.error);
} else {
//console.log(data);
setFilteredResults(data.data);
//console.log("size-->");
//console.log(data.size);
setSize(data.size);
setSkip(0);
}
});
};
....
....
useEffect(() => {
init();
console.log("initial");
loadFilteredResults(skip, limit, myFilters.filters);
console.log("skip");
}, []);
//full code of shop.js
import React, { useEffect, useState } from "react";
import Layout from "./Layout";
import Card from "./Card";
import { getCategories, getFilteredProducts } from "./apiCore";
import Checkbox from "./Checkbox";
import RadioBox from "./RadioBox";
import { prices } from "./fixedPrices";
const Shop = () => {
const [myFilters, setMyFilters] = useState({
filters: { category: [], price: [] }
});
const [categories, setCategories] = useState([]);
const [error, setError] = useState(false);
const [limit, setLimit] = useState(3);//prpduct lesss so use 3 but sir used 6
const [skip, setSkip] = useState(0);
const [size, setSize] = useState(0);
const [filteredResults, setFilteredResults] = useState([]);
const init = () => {
getCategories().then((data) => {
if (data.error) {
//console.log("error");
setError(data.error);
} else {
//console.log("set");
//console.log(data);
setCategories(data);
//console.log(data);
}
});
};
const loadFilteredResults = (newFilters) => {
//console.log(newFilters);
console.log("entry")
getFilteredProducts(skip, limit, newFilters).then((data) => {
console.log("entry1")
if (data.error) {
setError(data.error);
} else {
//console.log(data);
setFilteredResults(data.data);
//console.log("size-->");
//console.log(data.size);
setSize(data.size);
setSkip(0);
}
});
};
const loadMore = () => {
console.log("skip"+skip);
console.log("limit"+limit);
let toSkip = skip + limit;
console.log("toSkip"+toSkip);
getFilteredProducts(toSkip, limit, myFilters.filters).then((data) => {
//console.log("filter");
//console.log( myFilters.filters)
if (data.error) {
setError(data.error);
} else {
//console.log(filteredResults);
//console.log(data.data);
setFilteredResults([...filteredResults, ...data.data]);
//console.log("after");
//console.log(...filteredResults);
//console.log(filteredResults);
//console.log(filteredResults);
//console.log([...filteredResults])
//console.log([...filteredResults, ...data.data])
setSize(data.size);
setSkip(toSkip);
}
});
};
const loadMoreButton = () => {
return (
size > 0 &&
size >= limit && (
<button onClick={loadMore} className="btn btn-warning mb-5">
load more
</button>
)
);
};
useEffect(() => {
init();
//console.log(skip);
console.log("initial");
loadFilteredResults(skip, limit, myFilters.filters);
console.log("skip");
}, []);
const handleFilters = (filters, filterBy) => {
//console.log("SHOP", filters, filterBy);
const newFilters = { ...myFilters };
//console.log(newFilters);
newFilters.filters[filterBy] = filters;
//console.log(typeof(filters));
if (filterBy === "price") {
let priceValues = handlePrice(filters);
newFilters.filters[filterBy] = priceValues;
//console.log(priceValues);
}
//console.log(myFilters.filters);
loadFilteredResults(myFilters.filters);
setMyFilters(newFilters);
};
const handlePrice = (value) => {
const data = prices;
let array = [];
//console.log(value);
for (let key in data) {
if (data[key]._id === parseInt(value)) {
array = data[key].array;
}
}
return array;
};
// const x = (filters)=>{
// console.log("filters:"+filters);
// handleFilters(filters, "category")
// }
return (
<Layout
title="Shop Page"
description="search and buy books of your choice"
className="container-fluid"
>
<div className="row">
<div className="col-4">
<h4>Filter by categories</h4>
<ul>
{/* below will be show in list show we wrap it in unorder list */}
<Checkbox
categories={categories}
handleFilters={(filters) =>
handleFilters(filters, "category")
}
/>
</ul>
<h4>Filter by price range</h4>
<div>
<RadioBox
prices={prices}
handleFilters={(filters) => handleFilters(filters, "price")}
/>
</div>
</div>
<div className="col-8">
<h2 className="mb-4">Products</h2>
<div className="row">
{filteredResults.map((product, i) => (
<Card key={i} product={product} />
))}
</div>
<hr />
{loadMoreButton()}
</div>
</div>
</Layout>
);
};
export default Shop;
getFilteredProducts must be a Promise. Please read Using promises
Callbacks added with then() will never be invoked before the
completion of the current run of the JavaScript event loop.
i'm studying react and also an beginner. ploblem had caused when i was trying to convert HTMLCollection into an array. here's code.
const HeroSlide = ({ items }) => {
const heroSlide = useRef(null);
useEffect(() => {
const arr = Array.from(heroSlide.current.children);
arr.map((child) => {
child.className = 'text only';
});
console.log(arr);
// got 4 text-only divs. divs created by using map func are excluded
}, []);
return (
<div className="heroSlide" ref={heroSlide}>
<div>text only</div>
{items.map((e, i) => (
<div className="heroSlide__items">
<img
src={apiConfig.originalImage(e.backdrop_path)}
className="heroSlide__backgroundImage"
alt=""
/>
</div>
))}
<div>text only</div>
<div>text only</div>
<div>text only</div>
</div>
);
};
if i use setTimeout console.log works. but i need better way.
useEffect(() => {
setTimeout(() => {
const arr = Array.from(heroSlide.current.children);
console.log(arr);
}, 50);
}, []);
create a variable and push values as shown below -
const heroSlide = useRef(null);
let arr = []
useEffect(() => {
console.log(heroSlide.current.children);
console.log('array is',arr.push(...heroSlide.current.children))
console.log('arr',arr)
}, []);
I solved problem in this way. I passed the dynamic process to the child node to perform it. now i gets complete array.
const HeroSlideItems = ({ items }) => {
return (
<div className="heroSlide__items">
{items.map((e, i) => {
return (
<div className="heroSlide__items__index">
<img
src={apiConfig.originalImage(e.backdrop_path)}
className="heroSlide__backgroundImage"
alt=""
/>
</div>
);
})}
</div>
);
};
const HeroSlide = ({ items }) => {
const heroSlideRef = useRef(null);
useEffect(() => {
if (heroSlideRef.current) {
const children = Array.from(heroSlideRef.current.children);
children.forEach((c) => {
c.className = 'changed';
});
}
}, []);
return (
<div className="heroSlide" ref={heroSlideRef}>
<div>testclass</div>
<HeroSlideItems items={items} />
<div>testclass</div>
<div>testclass</div>
<div>testclass</div>
</div>
);
};
I encountered this error while writing this code. I thought since I am using map on array, this error occurred. so I tried to put 'key' there but couldn't find how to make it work.
If I remove that line and try console.log(posts) then I can see my posts list on console. Could anyone help me out here?
Thanks in advance!
export const list = async ctx => {
// query는 문자열이기 때문에 숫자로 변환해 주어야 함
// 값이 주어지지 않았다면 1을 기본으로 사용
const page = parseInt(ctx.query.page || '1', 10);
if (page < 1) {
ctx.status = 400;
return;
}
const { tag, username } = ctx.query;
// tag, username 값이 유효하면 객체 안에 넣고, 그렇지 않으면 넣지 않음
const query = {
...(username ? {'user.username': username} : {}),
...(tag ? { tags:tag } : {}),
};
try {
const posts = await Post.find(query)
.sort({_id: -1})
.limit(10)
.skip((page-1)*10)
.exec();
const postCount = await Post.countDocuments(query).exec();
ctx.set('Last-Page', Math.ceil(postCount / 10));
ctx.body = posts.map(post => ({
...post,
body: removeHtmlAndShorten(post.body),
}));
} catch (e) {
ctx.throw(500, e);
}
};
and below code is what it's rendering!
const PostItem = ({ post }) => {
const { publishedDate, user, tags, title, body, _id } = post;
return (
<PostItemBlock>
<h2>
<Link to={`/#${user.username}/${_id}`}>{title}</Link>
</h2>
<SubInfo username={user.username} publishedDate={new Date(publishedDate)} />
<Tags tags={tags} />
<p>{body}</p>
</PostItemBlock>
)
}
const PostList = ({ posts, loading, error, showWriteButton }) => {
// 에러 발생 시
// if (error) {
// return <PostListBlock>Error occured</PostListBlock>
// }
return (
<PostListBlock>
<WritePostButtonWrapper>
{showWriteButton && (
<Button cyan to="/write">
New post
</Button>
)}
</WritePostButtonWrapper>
{/* 로딩중이 아니고 포스트 배열이 존재할 때만 보여줌 */}
{!loading && posts && (
<div>
{posts.map(post => (
<PostItem post={post} key={post._id} />
))}
</div>
)}
</PostListBlock>
)
}
export default PostList;
try this :
<div>
{posts.map((post,index)=> (
<PostItem post={post} key={index} />
))}
</div>
so weirdly I solved this issue myself and I used this code below.
I used round bracket on 'post' while using map and it successfully brought data. Hope it can help someone who has similar issue with me XD
const postCount = await Post.countDocuments(query).exec();
ctx.set('Last-Page', Math.ceil(postCount / 10));
ctx.body = posts.map((post) => ({
...post,
body: removeHtmlAndShorten(post.body),
}));
} catch (e) {
ctx.throw(500, e);
}
};