I have 250 objects in the array i get from the axios get request, if i want to render out only 100 of the 250 objects but i want it to always be random can i do something like this
array.[Math.floor(Math.random()*items.length)]?
Here is my code.
export default function App() {
const [data, setData] = useState([])
const [searchCountry, setSearchCountry] = useState('')
const getData = async () => {
const { data } = await axios.get(`https://restcountries.com/v3.1/all`)
setData(data)
}
useEffect(() => {
getData()
}, [])
return (
data && (
<div className="App">
<SomeContext.Provider
value={
<h2>
<input
className="search-country"
placeholder="Search Country..."
onChange={(event) => {
setSearchCountry(event.target.value)
}}
/>
<ul id="countries-ul">
{data
.filter((country) => {
if (searchCountry === '') {
return country
} else if (
country.name.common
.toLowerCase()
.includes(searchCountry.toLowerCase())
)
return country
})
.map((country) => (
<li className="li-card" key={country.name.common}>
<Link to={`/country/${country.name.common}`}>
{country.name.common}
</Link>
</li>
))}
</ul>
</h2>
}
You can iterate your logic Math.floor(Math.random() * data.length) (which will pick a single item randomly in the array) 100 times. Be sure to remove a item in a array when it is picked if you don't wait it to be duplicated.
const getData = async () => {
const { data } = await axios.get(`https://restcountries.com/v3.1/all`)
const LENGTH = 100;
const picked = [];
for (var i = 0; i < LENGTH; i++) {
const index = Math.floor(Math.random() * data.length); // pick a item randomly
const removed = data.splice(index, 1); // remove it for preveting duplication
picked.push(removed[0]);
}
setData(picked);
}
Related
How to partially add data to an array and display it? The array is reset each time. Is it possible to save the previous state of an array before adding new data?
const ICONS_PER_PAGE = 5
const IconsList = () => {
const { iconArray, setIconArray } = useContext(IconArray)
const [triggerToLoad, setTriggerToLoad] = useState(true)
const [iconArrayLazy, setIconArrayLazy] = useState([])
const scrollHandler = (e) => {
let scrollGalleryValue = document.querySelector('.list__container').getBoundingClientRect().y + document.querySelector('.list__container').clientHeight - window.innerHeight
if (scrollGalleryValue <= 0 ) {
setTriggerToLoad(!triggerToLoad)
console.log(iconArrayLazy); // [] empty array
}
}
useEffect(() => {
setIconArrayLazy(iconArrayLazy => [...iconArrayLazy, ...iconArray.slice(iconArrayLazy.length, iconArrayLazy.length + ICONS_PER_PAGE)])
}, [triggerToLoad])
useEffect(() => {
setIconArrayLazy(iconArrayLazy => [...iconArrayLazy, ...iconArray.slice(iconArrayLazy.length, iconArrayLazy.length + ICONS_PER_PAGE)])
document.addEventListener('scroll', scrollHandler)
return function () {
document.removeEventListener('scroll', scrollHandler)
}
}, [])
return (
<>
<ul className="list__container">
{iconArrayLazy.map(({id, title, img}) =>
<li className="icon_container" key={id}>
<Link to={`/icon-${id}`}>
<img className="icon_container__image" src={img} alt={title} />
</Link>
</li>
)}
</ul>
</>
)
}
export default IconsList
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 trying to make an autocomplete search for Alpha Vantage API, but the array which should contain the matches (suggestion) always returns empty when I type in the input field and I don't know what could be the reason, I'm stuck for a while on this and would appreciate if someone could help me with this.
The related code here is mostly in useEffect and the inputHandler:
const Search = () => {
useEffect(() => {
const getSymbols = async () => {
const searchURL = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
const res = await axios.get(searchURL);
if(res) {
setSecurity(res.data.bestMatches);
if(security !== undefined && security.length > 0) {
let symbols = security.map(sec => sec['1. symbol'])
setAllSymbol(symbols);
}
}
}
getSymbols();
}, [])
console.log(allSymbol);
const inputHandler = (e) => {
setTextInput(e.target.value);
let matches = [];
if(textInput.length > 0) {
matches = allSymbol.filter(sym => {
const regex = new RegExp(`${textInput}`, "gi");
return sym.match(regex);
})
setSuggestion(matches);
}
console.log(suggestion);
setTextInput(e.target.value);
}
const showData = async (e) => {
e.preventDefault();
const searchURL = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
const monthlyURL = `https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
try {
const res = await axios.get(searchURL);
const data = await axios.get(monthlyURL);
if(res) {
setTickers(res.data.bestMatches[0]);
setSymbol(res.data.bestMatches[0]['1. symbol']);
setSecurity(res.data.bestMatches);
if(data) {
const monthlyTimeSeries = Object.values(data.data['Monthly Time Series']);
const result = [monthlyTimeSeries[1]];
const resultValues = Object.keys(result[0]).map(key => {
return Math.floor(result[0][key]);
})
setPrices(resultValues);
}
}
} catch(err) {
console.log(err)
}
setDailyPrices([]);
setWeeklyPrices([]);
setIntraPrices([]);
}
return (
<StyledSearch>
<div className="wrapper">
<h1>Security Price Monitor App </h1>
<form onSubmit={showData} className="search-form">
<input type="text" value={textInput} onChange={inputHandler} placeholder='Enter Stock Symbol (GOOG, MSFT)'/>
<button type="submit">Search</button>
</form>
</div>
{prices.length > 0 && (
<>
<Table prices={prices} symbol={symbol}/>
<TimeFrames symbol={symbol} textInput={textInput} weeklyPrices={weeklyPrices} setWeeklyPrices={setWeeklyPrices} dailyPrices={dailyPrices} setDailyPrices={setDailyPrices} intraPrices={intraPrices} setIntraPrices={setIntraPrices} />
</>
)}
</StyledSearch>
)
}
I have a list and this list has several elements and I iterate over the list. For each list I display two buttons and an input field.
Now I have the following problem: as soon as I write something in a text field, the same value is also entered in the other text fields. However, I only want to change a value in one text field, so the others should not receive this value.
How can I make it so that one text field is for one element and when I write something in this text field, it is not for all the other elements as well?
import React, { useState, useEffect } from 'react'
import axios from 'axios'
function Training({ teamid }) {
const [isTrainingExisting, setIsTrainingExisting] = useState(false);
const [trainingData, setTrainingData] = useState([]);
const [addTraining, setAddTraining] = useState(false);
const [day, setDay] = useState('');
const [from, setFrom] = useState('');
const [until, setUntil] = useState('');
const getTrainingData = () => {
axios
.get(`${process.env.REACT_APP_API_URL}/team/team_training-${teamid}`,
)
.then((res) => {
if (res.status === 200) {
if (typeof res.data !== 'undefined' && res.data.length > 0) {
// the array is defined and has at least one element
setIsTrainingExisting(true)
setTrainingData(res.data)
}
else {
setIsTrainingExisting(false)
}
}
})
.catch((error) => {
//console.log(error);
});
}
useEffect(() => {
getTrainingData();
}, []);
const deleteTraining = (id) => {
axios
.delete(`${process.env.REACT_APP_API_URL}/team/delete/team_training-${teamid}`,
{ data: { trainingsid: `${id}` } })
.then((res) => {
if (res.status === 200) {
var myArray = trainingData.filter(function (obj) {
return obj.trainingsid !== id;
});
//console.log(myArray)
setTrainingData(() => [...myArray]);
}
})
.catch((error) => {
console.log(error);
});
}
const addNewTraining = () => {
setAddTraining(true);
}
const addTrainingNew = () => {
axios
.post(`${process.env.REACT_APP_API_URL}/team/add/team_training-${teamid}`,
{ von: `${from}`, bis: `${until}`, tag: `${day}` })
.then((res) => {
if (res.status === 200) {
setAddTraining(false)
const newTraining = {
trainingsid: res.data,
mannschaftsid: teamid,
von: `${from}`,
bis: `${until}`,
tag: `${day}`
}
setTrainingData(() => [...trainingData, newTraining]);
//console.log(trainingData)
}
})
.catch((error) => {
console.log(error);
});
}
const [editing, setEditing] = useState(null);
const editingTraining = (id) => {
//console.log(id)
setEditing(id);
};
const updateTraining = (trainingsid) => {
}
return (
<div>
{trainingData.map((d, i) => (
<div key={i}>
Trainingszeiten
<input class="input is-normal" type="text" key={ d.trainingsid } value={day} placeholder="Wochentag" onChange={event => setDay(event.target.value)} readOnly={false}></input>
{d.tag} - {d.von} bis {d.bis} Uhr
<button className="button is-danger" onClick={() => deleteTraining(d.trainingsid)}>Löschen</button>
{editing === d.trainingsid ? (
<button className="button is-success" onClick={() => { editingTraining(null); updateTraining(d.trainingsid); }}>Save</button>
) : (
<button className="button is-info" onClick={() => editingTraining(d.trainingsid)}>Edit</button>
)}
<br />
</div>
))}
)
}
export default Training
The reason you see all fields changing is because when you build the input elements while using .map you are probably assigning the same onChange event and using the same state value to provide the value for the input element.
You should correctly manage this information and isolate the elements from their handlers. There are several ways to efficiently manage this with help of either useReducer or some other paradigm of your choice. I will provide a simple example showing the issue vs no issue with a controlled approach,
This is what I suspect you are doing, and this will show the issue. AS you can see, here I use the val to set the value of <input/> and that happens repeatedly for both the items for which we are building the elements,
const dataSource = [{id: '1', value: 'val1'}, {id: '2', value: 'val2'}]
export default function App() {
const [val, setVal]= useState('');
const onTextChange = (event) => {
setVal(event.target.value);
}
return (
<div className="App">
{dataSource.map(x => {
return (
<div key={x.id}>
<input type="text" value={val} onChange={onTextChange}/>
</div>
)
})}
</div>
);
}
This is how you would go about it.
export default function App() {
const [data, setData]= useState(dataSource);
const onTextChange = (event) => {
const id = String(event.target.dataset.id);
const val = String(event.target.value);
const match = data.find(x => x.id === id);
const updatedItem = {...match, value: val};
if(match && val){
const updatedArrayData = [...data.filter(x => x.id !== id), updatedItem];
const sortedData = updatedArrayData.sort((a, b) => Number(a.id) - Number(b.id));
console.log(sortedData);
setData(sortedData); // sorting to retain order of elements or else they will jump around
}
}
return (
<div className="App">
{data.map(x => {
return (
<div key={x.id}>
<input data-id={x.id} type="text" value={x.value} onChange={onTextChange}/>
</div>
)
})}
</div>
);
}
What im doing here is, finding a way to map an element to its own with the help of an identifier. I have used the data-id attribute for it. I use this value again in the callback to identify the match, update it correctly and update the state again so the re render shows correct values.