How to get different values ​when generating an element through Object.keys? - reactjs

I create a sidebar through the object.keys(sidebars), and in each of them there are various menu items that I get from the database, how to change these elements in state.items? To access the item field during generation and display these menu items?
Now I do like this, but nothing comes of it ...
const [sidebarItemsLeagues, setSidebarItemsLeagues] = useState(null); // SIDEBAR 1 ITEMS
const [sidebarItemsCountries, setSidebarItemsCountries] = useState(null); // SIDEBAR 2 ITEMS
const [sidebars] = useState({
leagues: {
title: "SIDEBAR 1",
items: sidebarItemsLeagues // null...
},
countries: {
title: "SIDEBAR 2",
items: sidebarItemsCountries // null...
}
});
useEffect(() => {
api.getTestLeagues() // get sidebar 1 items
.then(data => setSidebarItemsLeagues(data));
}, []);
useEffect(() => {
api.getTestCountries() // get sidebar 2 items
.then(data => setSidebarItemsCountries(data));
}, [])
const onHandleSidebarView = () => { // generate sidebars
return (
Object.keys(sidebars).map((sidebar, idx) => {
const sidebarControl = sidebars[sidebar];
return (
<div className="sidebar" key={idx}>
<div className="sidebar__header">
<p>{sidebarControl.title}</p>
</div>
<div className="sidebar__items">
// SIDEBAR 1 && SIDEBAR 2 ITEMS
</div>
</div>
);
})
);
};

You can use [] notation back onto your object to access its corresponding property dynamically. I changed the map to use key for more clarity
Object.keys(sidebars).map((key, idx) => {
return (
<div className="sidebar" key={idx}>
<div className="sidebar__header">
<p>{sidebars[key].title}</p>
</div>
<div className="sidebar__items">
{
// SIDEBAR ITEMS (assuming it contains an array)
sidebars[key].items.map(itemElement => {
// return some elements
}
}
</div>
</div>
);
})

Related

How to check if the value of one obj exists in the another obj?

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

Coloring the appropriate item from the list after clicking. [Next Js]

I want to create a function that will color the hearts when clicked.
I wrote a function that prints out elements for me, but when I click on any heart, it colors them all.
Where could the problem be?
My code:
const \[userInput, setUserInput\] = useState("");
const \[list, setList\] = useState(\[\]);
const \[hearth, setHearth\] = useState(false);
const \[active, setActive\] = useState(-1);
const handleChange = (e) =\> {
e.preventDefault();
setUserInput(e.target.value);
};
const handleSubmit = (e) =\> {
e.preventDefault();
setList(\[userInput, ...list\]);
setUserInput("");
};
const wishList = (e) =\> {
setHearth(!hearth);
};
useEffect(() =\> {}, \[userInput, list\]);
return (
\<div className="favMusic"\>
<h1>FavMusicList</h1>
\<form\>
\<input value={userInput} onChange={handleChange} type="text" /\>
\<button onClick={handleSubmit}\>Submit\</button\>
\</form\>
<ul className="favMusic__list">
{list.map((i, idx) => {
console.log(idx);
return (
<li key={idx}>
{i}{" "}
<div
id={idx}
onClick={() => wishList(idx)}
className={"hearth" + " " + (hearth ? "true" : "false")}>
<AiOutlineHeart
/>
</div>
</li>
);
})}
</ul>
</div>
I have tried all possible ways from setState to others found on the net but I have no idea how to solve it
Here's a working demo.
Assuming your state data is an array of items, each with its own boolean property indicating whether it's been "liked" by the user:
[
{
id: 1,
liked: true,
title: 'ListItem 1',
},
{
id: 2,
liked: false,
title: 'ListItem 2',
},
// ...
]
Then in your click handler, you'd want to loop over each of the objects to find the item with the corresponding id to change just the boolean property for that one item. For example:
const handleClick = (id) => {
const newLikes = items.map((item) => {
// check the current element's id against the
// id passed to the handler
if (item.id === id) {
// if it matches, update the liked property
// and return the modified object
return { ...item, liked: !item.liked };
}
// if it doesn't match, just return the
// original object
return item;
});
// update state with the new data
setItems(newLikes);
};

How can I convert HTMLCollection to Array instead of an empty array in React?

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>
);
};

Struggling in passing data from Child to Parent React Component. Cannot update a component (`App`) while rendering a different component (`Table`)

My objective is to sort table's data according to the column clicked.
In order to to accomplish this goal, I need to pass the information about the header clicked from the child component "Table" to the parent component "App".
This is from the child component Table :
const [keyclicked, setKeyclicked] = React.useState("");
const [sortOptions, setSortOptions] = React.useState({
first: "",
second: ""
});
const modalFunct = React.useMemo(() => {
if (document.getSelection(keyclicked.target).focusNode !== null) {
console.log(
document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase()
);
let newsorting = sortOptions;
if (sortOptions.first !== "") {
newsorting.second = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
} else {
newsorting.first = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
}
setSortOptions(newsorting);
selectSorter(
document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase()
);
}
}, [keyclicked]);
const renderHeader = () => {
let headerElement = ["id", "name", "email", "phone", "operation"];
return headerElement.map((key, index) => {
return (
<th onClick={setKeyclicked} key={index}>
{key.toUpperCase()}
</th>
);
});
};
const renderBody = () => {
console.log("renderBody-employees: ", employees);
return employees.map(({ id, name, email, phone }) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{email}</td>
<td>{phone}</td>
<td className="operation">
<button className="button" onClick={() => removeData(id)}>
Delete
</button>
</td>
</tr>
);
});
};
return (
<>
<h1 id="title">Table</h1>
<h3>
{" "}
Lets go for a <FaBeer /> ?{" "}
</h3>
<table id="employee">
<thead>
<tr>{renderHeader()}</tr>
</thead>
<tbody>{renderBody()}</tbody>
</table>
</>
);
};
export default Table;
This is from App.js :
import Table from "./Table";
const [selectedSortingOption, SetSelectedSortingOption] = React.useState(
null
);
return (
<div className="App">
<div align="center">
<button onClick={addSingleEmployee}>AddEmployee</button>
<Select
defaultValue={selectedSortingOption}
onChange={SetSelectedSortingOption}
options={sortingOptions}
/>
</div>
<div className="scrollable">
<Table
table_data={sortedData}
row_data={newEmployee}
basePageLink={""}
removeData={removeRaw}
selectSorter={selectHowToSort}
/>
</div>
<div align="center">
<button onClick={emptyTable}>EmptyTable</button>
</div>
</div>
);
}
When clicking on the email header for example I get this output in the console log:
`email` : which is correct
and this warning - error message:
Warning: Cannot update a component (`App`) while rendering a different component (`Table`). To locate the bad setState() call inside `Table`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
Table#https://oqx8ut.csb.app/src/Table/index.jsx:23:15
div
div
App#https://oqx8ut.csb.app/src/App.js:168:33
Table/index.jsx:23 refers to this line:
React.useEffect(() => {
setEmployees(table_data);
return () => {
// clean-up function
};
}, [table_data]);
while App.js:168 refers to this:
const [selectedSortingOption, SetSelectedSortingOption] = React.useState(
null
);
I tried also to do this in the Child Component "Table" :
const [sortOptions, setSortOptions] = React.useState({
first: "",
second: ""
});
//const modalFunct = (key_clicked) => {
const modalFunct = React.useMemo(() => {
//console.log(keyclicked.target);
//console.log(document.getSelection(keyclicked.target).focusNode);
if (document.getSelection(keyclicked.target).focusNode !== null) {
console.log(
//selectSorter(
document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase()
);
let newsorting = sortOptions;
if (sortOptions.first !== "") {
newsorting.second = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
} else {
newsorting.first = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
}
setSortOptions(newsorting);
//selectSorter(
//document
//.getSelection(keyclicked.target)
//.focusNode.wholeText.toLowerCase()
//);
}
}, [keyclicked]);
const memoizedSelectSorter = React.useMemo(() => {
selectSorter(sortOptions);
}, [sortOptions]);
but still get the same error
What am I doing wrong? How to pass the email info (the info about which header has been clicked) from the Child component "Table" to the Parent Component "App" where the data is going to be sorted?
You search in your code this line: return employees.map(({ id, name, email, phone }) => {, you put return before Array.map() will give you array not a JSX syntax. Try to remove return in that line:
const renderBody = () => {
console.log("renderBody-employees: ", employees);
return employees.map(({ id, name, email, phone }) => { //<== remove this return here, put "?" in employees?.map to prevent crash app
return (
<tr key={id}>
<td>{id}</td>
....
Maybe table_data in your dependency make Table Component infinity re-render cause React busy to render this component, try to remove it:
React.useEffect(() => {
setEmployees(table_data);
return () => {
// clean-up function
};
}, []); // <== Had remove table_data in dependency

How can I filter through what is displayed with useEffect?

I am a bit lost on what to do for the next step. I have managed to display the content but I can't seem to get it to filter with a click. It was easy enough to do with a different api , i followed webdevsimplified but this i can't work out and I am at my wits end!
All I want is to filter through the mapped api. for example if I check 3, it should show me only 3 starRating. Can anybody offer me some advice please.
App.js
import { useEffect, useState, useRef } from 'react'
import Header from './components/Header';
import SearchBar from './components/SearchBar';
export default function App() {
const [hotelRooms, setHotelRooms] = useState([]);
const fetchHotels = async () => {
const res = await fetch('https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG')
const hotels = await res.json()
const hotelRooms = []
for(const hotel of hotels) {
const res = await fetch(`https://obmng.dbm.guestline.net/api/roomRates/OBMNG/${hotel.id}`)
const info = await res.json()
hotelRooms.push({ hotel, rooms: info.rooms })
}
setHotelRooms(hotelRooms)
}
useEffect(() => {
fetchHotels()
}, [])
return (
<div className="App">
<Header/>
{
hotelRooms.map(h => (
<div>
<input value={"1"} type="checkbox" onChange={}/>
<input value={"Adults"}type="checkbox" onChange={}/>
<h2> Name: {h.hotel.name}</h2>
<p> Description: {h.hotel.description}</p>
<p> Rating: {h.hotel.starRating}</p>
<p> Postcode: {h.hotel.postcode}</p>
<p> City: {h.hotel.town}</p>
<img src={h.hotel.images}/>
<p style={{ fontWeight: 'bold' }}>Rooms:</p>
{
h.rooms.map(room => (
<div>
<h5>Occupancy</h5>
<div> Adults: {room.occupancy.maxAdults}</div>
<div> Children: {room.occupancy.maxChildren}</div>
<div> Maximum guests: {room.occupancy.maxOverall}</div>
<div> Room type: {room.name}</div>
<img src={room.images}/>
</div>
))
}
</div>
))
}
</div>
);
}
You should have a state that saves the filtered properties.
const [filter, setFilter] = useState({ ratings: ["1", "2", "3", "4", "5"] });
When you show the checkboxes add a name to them and the respective values.
Remember when you use .map in render, add an unique key to the out most tag.
<div>
{["1", "2", "3", "4", "5"].map((star) => (
<div key={"input-" + star}>
<input
id={"rated" + star}
value={star}
name="ratings"
type="checkbox"
checked={filter.ratings.includes(star)}
onChange={handleRatingFilter}
/>
<label htmlFor={"rated" + star}>Rated {star} star</label>
</div>
))}
</div>
Now in the onChange handler, update the state according to the checkboxes:
const handleRatingFilter = (e) => {
if (e.target.checked) {
// adding value
const temp = [...filter.ratings];
temp.push(e.target.value);
setFilter({ ...filter, ratings: temp });
} else {
// removing value
setFilter({
...filter,
ratings: [...filter.ratings.filter((v) => v !== e.target.value)]
});
}
};
Finally, when you use .map on hotelRooms you can filter the list before mapping it.
{hotelRooms
.filter((h) => filter.ratings.includes(h.hotel.starRating))
.map((h) => (
<div key={h.hotel.name}>
stuff
</div>
))
}
Working CodeSandbox
If I am understanding your question correctly, you want it to re-render after you update hotelRooms? If this is correct, when you first render it, the value is [], a blank array. And in here :
useEffect(() => {
fetchHotels()
}, [])
That last bit [], runs once after rendering. Therefore in your case (if my assumption is correct), you will want to change it to, as you want it to re-render each time hotelRooms value change
useEffect(() => {
fetchHotels()
}, [hotelRooms])

Resources