games.filter is not a function || React hooks component not receiving data - javascript

Up till this point, I have been able to fetch my data from MongoDB and view the data in my frontend console in react but games.filter is stopping my component from receiving the data and display them in the browser.
I have tried using Object.values(games).filter; the error goes away but it doesn't display the data still.
Here is a screenshot when i inspected the components
I can see the data in GamesByCategory component here
Here's is another image:
Datas in GamesBox is undefined
Here's my code
import React, {useState, useRef, useContext} from 'react'
import { useScrollPosition } from '#n8tb1t/use-scroll-position'
import GameBox from '../objects/GameBox'
import GamesContext from '../../contexts/GamesContext'
const GamesByCategories = () => {
const { games, setGames } = useContext(GamesContext)
const [currentCategory, setCurrentCategory] = useState(0)
const [categories, setCategories] = useState([
{id: 0, name: 'all', isActive: true},
{id: 1, name: 'gadgets', isActive: false},
{id: 2, name: 'sports', isActive: false},
{id: 3, name: 'family', isActive: false},
])
const categoriesTab = categories.map(category => {
var active = category.isActive ? 'active' : ''
return(
<div key={category.id} onClick={() => filterCategory(category.id)} className={"tab-element " + active } >
<span>{category.name}</span>
</div>
)
})
//filter and populate the games list by current category
// THIS IS WHERE I AM HAVING ISSUES
const gamesList = Object.values(games).filter(game => !game.isVersion )
.filter(game => (game.category === (currentCategory) || currentCategory === 0))
.map(game => {
return (
<GameBox id={game.id} image={game.image} name={game.name} price={game.price} isNew={game.gameIsNew} />
)
})
const filterCategory = (categoryId) => {
//update the current category
var updatedCategories = categories.map(category => {
if(category.id === categoryId){
category.isActive = true
}else{
category.isActive = false
}
return category
})
setCurrentCategory(categoryId)
setCategories(updatedCategories)
}
//add some styles to sticky categories tab
const [isCategoriesTabSticky, setIsCategoriesTabSticky] = useState(false)
const categoriesTabRef = useRef(null)
const stickyStyles = isCategoriesTabSticky ? "is-sticky" : ""
useScrollPosition( ({currPos}) =>{
if(-(currPos.y) >= categoriesTabRef.current.offsetTop){
setIsCategoriesTabSticky(true)
}else{
setIsCategoriesTabSticky(false)
}
})
return (
<div>
<div id="game-categories-tab" ref={categoriesTabRef} className={"flex justify-center mt-10 mb-8 custom-tabs " + stickyStyles}>
<div className="tab-container">
<div className="flex">
{categoriesTab}
</div>
</div>
</div>
<div className="grid justify-center categories-games">
<div className="flex flex-wrap justify-center categories-games-container">
{ gamesList }
</div>
</div>
</div>
)
}
export default GamesByCategories;
Kindly reach-out and help.
Thank you in advance

Related

How can I toggle one icon which triggers the disabling of another icon in React?

I am developing an app with react and redux toolkit. I am using an API. the part that I would like help with is this:
I have a favorite Icon. this icon takes a copy of the movie and displays it in the favorite section. There is another icon which is for watched movies. so the idea is that if I click on watched it should disable only the favorite icon of the movie card I clicked. however, it disables all favorite icons for all movie cards. The watched icon is referred to as Eye the favorite icon is referred to as star
This is the eye component (Watched)
import { useState } from "react";
import { BsEyeSlash, BsEyeFill } from "react-icons/bs";
import { useDispatch, useSelector } from "react-redux";
import {
add_watched,
remove_watched,
add_occupied,
remove_occupied,
} from "../features/animeSlice";
const Eye = ({ anime, type }) => {
const [active_eye, setActive_eye] = useState(false);
const dispatch = useDispatch();
const toggle_eye = () => {
if (!active_eye) {
setActive_eye((prev) => {
return !prev;
});
dispatch(add_watched(anime));
dispatch(add_occupied(type));
} else {
setActive_eye((prev) => {
return !prev;
});
dispatch(remove_watched(anime?.mal_id));
dispatch(remove_occupied());
}
};
return (
<div>
{!active_eye ? (
<BsEyeSlash
className="text-xl text-green-500 icons_1"
onClick={toggle_eye}
/>
) : (
<BsEyeFill
className={"text-xl text-red-500 icons_1"}
onClick={toggle_eye}
/>
)}
</div>
);
};
export default Eye;
This is the Star Component (Favorite)
import { useState } from "react";
import { AiOutlineStar, AiFillStar } from "react-icons/ai";
import { add_favourite, remove_favourite } from "../features/animeSlice";
import { useDispatch, useSelector } from "react-redux";
const Star = ({ anime }) => {
const { value} = useSelector((state) => state.anime);
const [active_star, setActive_star] = useState(false);
const dispatch = useDispatch();
const toggle_star = () => {
if (!active_star) {
setActive_star((prev) => {
return !prev;
});
dispatch(add_favourite(anime));
} else {
setActive_star((prev) => {
return !prev;
});
dispatch(remove_favourite(anime?.mal_id));
}
};
return (
<div>
{!active_star ? (
<AiOutlineStar
className={
value === "occupied"
? "text-xl text-gray-300 pointer-events-none"
: "text-xl text-yellow-500 icon_1"
}
onClick={toggle_star}
/>
) : (
<AiFillStar
className={
value === "occupied"
? "text-xl text-gray-300 pointer-events-none"
: "text-xl text-yellow-500 icon_1"
}
onClick={toggle_star}
/>
)}
</div>
);
};
export default Star;
this is the redux toolkit slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
favourite: [],
watched: [],
value: "",
page:""
};
const animeSlice = createSlice({
name: "anime",
initialState,
reducers: {
add_favourite(state, { payload }) {
state.favourite.push(payload);
},
remove_favourite(state, { payload }) {
state.favourite = state.favourite.filter(
(anime) => anime?.mal_id !== payload
);
},
add_watched(state, { payload }) {
state.watched.push(payload);
},
remove_watched(state, { payload }) {
state.watched = state.watched.filter((anime) => anime?.mal_id !== payload);
},
add_occupied(state, { payload }) {
state.value = payload;
},
remove_occupied(state) {
state.value = "";
},
pageNumber(state, { payload }) {
state.page = payload
}
},
});
export const {
add_favourite,
remove_favourite,
add_watched,
remove_watched,
add_occupied,
remove_occupied,
pageNumber
} = animeSlice.actions;
export default animeSlice.reducer;
This is the component that holds the component of eye and star
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import Eye from "./Eye";
import Star from "./Star";
const TopAnime = ({ anime }) => {
//pagination
//
//
let colorYear = (year) => {
if (year === "N/A") {
return "text-violet-500";
} else {
return "";
}
};
return (
<section className="grand px-4 pt-2 pb-5 w-64 bg-white/5 bg-opacity-80 backdrop-blur-sm rounded-lg cursor-pointer font-poppins animate-slideup">
<div className="pb-1 wrapper_icons">
<div className="wrapper_hover">
<Eye anime={anime} type="occupied" />
<Star anime={anime} />
</div>
</div>
<Link to={`/anime/${anime?.mal_id}`}>
<div className="wrapper_1 flex flex-col items-center justify-center">
<div className="h-[313px] w-[219px]">
<img
src={anime?.images?.jpg?.large_image_url}
alt={anime?.title}
className="h-full w-full"
/>
</div>
</div>
<div className="flex flex-col mt-3">
<p className="text-sm text-white truncate mx-1">
{anime?.title_english ? anime?.title_english : anime?.title}
</p>
<div className="flex justify-between items-center text-sm text-yellow-500 mx-1">
<p className={colorYear(anime?.year ? anime?.year : "N/A")}>
{anime?.year ? anime?.year : "N/A"}
</p>
<p
className={
anime?.score <= 7
? "text-cyan-500"
: anime?.score <= 5
? "text-red-600"
: "text-green-500"
}
>
{anime?.score}
</p>
</div>
</div>
</Link>
</section>
);
};
export default TopAnime;
This is where TopAnime is rendered
import { useGetAnimeQuery } from "../features/API";
import { useDispatch, useSelector } from "react-redux";
import { useState, useEffect } from "react";
import TopAnime from "../components/TopAnime";
import Spinner from "../components/Spinner";
import { NavLink, Link } from "react-router-dom";
import Paginator from "../components/Paginator";
//import ReactPaginate from "react-paginate";
import Pagination from "#mui/material/Pagination";
const Home = () => {
const [page, setPage] = useState(1);
const {
data: manga = [],
isLoading,
isFetching,
error,
} = useGetAnimeQuery(page);
const { data, pagination } = manga;
//destructuring objects
//const { data, pagination } = manga;
//const top_anime = data;
const total = Math.ceil(pagination?.items?.total / 24)
//const current_page = pagination?.current_page;
//const per_page = pagination?.items?.per_page;
//const { items: pages } = total;
/* let fetchData = async (page = 1) => {
let res = await fetch(
`https://api.jikan.moe/v4/top/anime?page=${page}&limit=24`
);
let query = await res.json();
const { data, pagination } = query;
let totalPages = Math.ceil(pagination?.items.total / 24);
setPageCount(totalPages);
setData(data);
};
useEffect(() => {
fetchData();
}, []);
*/
import { useGetAnimeQuery } from "../features/API";
import { useDispatch, useSelector } from "react-redux";
import { useState, useEffect } from "react";
import TopAnime from "../components/TopAnime";
import Spinner from "../components/Spinner";
import { NavLink, Link } from "react-router-dom";
import Paginator from "../components/Paginator";
//import ReactPaginate from "react-paginate";
import Pagination from "#mui/material/Pagination";
const Home = () => {
const [page, setPage] = useState(1);
const {
data: manga = [],
isLoading,
isFetching,
error,
} = useGetAnimeQuery(page);
const { data, pagination } = manga;
//destructuring objects
//const { data, pagination } = manga;
//const top_anime = data;
const total = Math.ceil(pagination?.items?.total / 24)
//const current_page = pagination?.current_page;
//const per_page = pagination?.items?.per_page;
//const { items: pages } = total;
/* let fetchData = async (page = 1) => {
let res = await fetch(
`https://api.jikan.moe/v4/top/anime?page=${page}&limit=24`
);
let query = await res.json();
const { data, pagination } = query;
let totalPages = Math.ceil(pagination?.items.total / 24);
setPageCount(totalPages);
setData(data);
};
useEffect(() => {
fetchData();
}, []);
*/
const handleChange = (event, value) => {
setPage(value);
};
const display = data?.map((anime) => (
<TopAnime anime={anime} key={anime.mal_id} />
));
//const pageCount = Math.ceil(pagination?.items?.total / 24);
if (isLoading) {
return <Spinner color_1={"#141e30"} color_2={"#243b55"} />;
} else if (isFetching) {
return <Spinner color_1={"#141e30"} color_2={"#243b55"} />;
} else if (error) {
console.log("ERROR =>", error.message);
}
return (
<section className="bg-gradient-to-r from-[#141e30] to-[#243b55]">
<div className="container font-poppins">
<div className="grid grid-cols-4 gap-3 place-items-center px-20">
{/* {top_anime &&
top_anime?.map((anime) => (
<TopAnime anime={anime} key={anime.mal_id} />
))} */}
{display}
</div>
<div className="button text-yellow-500 flex items-center justify-center mt-2 pb-2 cursor-pointer">
{/* <Paginator paginated={paginated} NumP={pagination?.last_visible_page} /> */}
{/* <ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
onPageChange={(page) => fetchData(page.selected + 1)}
pageCount={pageCount}
className="flex space-x-2"
activeClassName="active"
/> */}
<Pagination count={total} page={page} onChange={handleChange} defaultPage={1} boundaryCount={3} color="secondary" sx={{button:{color:'#ffffff'}}} />
</div>
</div>
</section>
);
};
export default Home;
Issue
The issue with the code/current implementation is that there is only one state.anime.value and one "occupied" value. Each time a movie's "watched" status is toggled the Redux state sets/unsets the single state.anime.value state. In other words, you can toggle N movies "watched" and the state.anime.value value is "occupied", but then toggling just 1 movie back to "unwatched" and state.anime.value is reset back to "". All the Star components read this single state value and this is why the stars all toggle together.
Solution
If you want a specific movie to toggle the Star component when the Eye icon is toggled then I think the solution is a bit simpler than you are making it out to be in your code.
The "eye" and "star" components effectively duplicate your Redux state and don't synchronize well with it. It's often considered a React anti-pattern to duplicate state. The state.anime.favourite and state.anime.watched arrays are all the app needs to know what has been marked "watched" and what has been favourited.
Update the animeSlice to store the anime.mal_id properties in objects instead of arrays to provide O(1) lookup in the UI. Remove the anime.value state as it's unnecessary.
animeSlice.js
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
favourite: {},
watched: {},
page: ""
};
const animeSlice = createSlice({
name: "anime",
initialState,
reducers: {
add_favourite(state, { payload }) {
state.favourite[payload] = true;
},
remove_favourite(state, { payload }) {
delete state.favourite[payload];
},
add_watched(state, { payload }) {
state.watched[payload] = true;
delete state.favourite[payload]; // un-star when adding as watched
},
remove_watched(state, { payload }) {
delete state.watched[payload];
},
pageNumber(state, { payload }) {
state.page = payload;
}
}
});
export const {
add_favourite,
remove_favourite,
add_watched,
remove_watched,
pageNumber
} = animeSlice.actions;
export default animeSlice.reducer;
Update the Eye and Star components to read the true state from the store. These components should pass anime.mal_id to the dispatched actions and use anime.mal_id to check the watched/favourites map objects.
Eye
import { BsEyeSlash, BsEyeFill } from "react-icons/bs";
import { useDispatch, useSelector } from "react-redux";
import { add_watched, remove_watched } from "../features/animeSlice";
const Eye = ({ anime }) => {
const dispatch = useDispatch();
const { watched } = useSelector((state) => state.anime);
const isWatched = watched[anime.mal_id];
const toggle_eye = () => {
dispatch((isWatched ? remove_watched : add_watched)(anime.mal_id));
};
const Eye = isWatched ? BsEyeFill : BsEyeSlash;
const className = [
"text-xl icons_1",
isWatched ? "text-green-500" : "text-red-500"
].join(" ");
return (
<div>
<Eye className={className} onClick={toggle_eye} />
</div>
);
};
export default Eye;
Star
import { AiOutlineStar, AiFillStar } from "react-icons/ai";
import { add_favourite, remove_favourite } from "../features/animeSlice";
import { useDispatch, useSelector } from "react-redux";
const Star = ({ anime }) => {
const { favourite, watched } = useSelector((state) => state.anime);
const dispatch = useDispatch();
const isFavorited = favourite[anime.mal_id];
const isWatched = watched[anime.mal_id];
const toggle_star = () => {
dispatch((isFavorited ? remove_favourite : add_favourite)(anime.mal_id));
};
const Star = isFavorited ? AiFillStar : AiOutlineStar;
const className = [
"text-xl",
isWatched ? "text-gray-300 pointer-events-none" : "text-yellow-500 icon_1"
].join(" ");
return (
<div>
<Star className={className} onClick={toggle_star} />
</div>
);
};
export default Star;

Uncaught TypeError: Cannot read properties of undefined (reading 'longdescription_title') when page is refreshed

I'm pretty new to react, and I am trying to make an Accordion Component with multiple dropdowns. I am trying to load my data from my database. I had thought I got it to work because it showed my data in the correct areas, but when I refreshed the page I got an Uncaught TypeError: Cannot read properties of undefined (reading 'longdescription_title') error. I'm not sure why this is happening, and I would really appreciate any help or advice on how to fix this problem.
Thank you!
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { detailsProduct } from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import '../components/Accordion.css'
import { IconContext } from 'react-icons';
import { FiPlus, FiMinus } from 'react-icons/fi';
export default function ProductScreen(props) {
const dispatch = useDispatch();
const productId = props.match.params.id;
const [accordionItems, setAccordionItems] = useState([]);
const [accordionTitles, setAccordionTitles] = useState([]);
const [clicked, setClicked] = useState(false);
const productDetails = useSelector((state) => state.productDetails);
const { loading, error, product } = productDetails;
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
useEffect(() => {
if (product) {
const accordionItems = [product.how_to_use];
accordionItems.unshift(product.ingredients);
accordionItems.unshift(product.longdescription);
setAccordionItems(accordionItems);
}
}, [product]);
useEffect(() => {
if (product) {
const accordionTitles = [product.how_to_use_title];
accordionTitles.unshift(product.ingredients_title);
accordionTitles.unshift(product.longdescription_title);
setAccordionTitles(accordionTitles);
}
}, [product]);
const Items = [...accordionItems];
const Titles = [...accordionTitles];
const accordion = [
{title: product.longdescription_title, body: product.longdescription},
{title: product.ingredients_title, body: product.ingredients},
{title: product.how_to_use_title, body: product.how_to_use},
]
const toggle = index => {
if (clicked === index) {
return setClicked(null);
}
setClicked(index);
};
return (
<div>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<Link to="/body">Back to result</Link>
<div className="row top">
<div className="col-1">
<ul>
<li>
<h1>{product.name}</h1>
</li>
<li>
<div>
<IconContext.Provider value={{ color: 'black', size: '2vw' }}>
<div className="accordionSection">
<div className = "container">
{accordion && accordion.length ? (
accordion.map((item, index) => {
return (
<>
<div className = "wrap" onClick={() => toggle(index)} key={index}>
<h1>{item.title}</h1>
<span>{clicked === index ? <FiMinus /> : <FiPlus />}</span>
</div>
{clicked === index ? (
<div className="dropdown">
<p>{item.body}</p>
</div>
) :
null}
</>
);
})
) : (
<></>
)}
</div>
</div>
</IconContext.Provider>
</div>
</li>
</ul>
</div>
<li>
<button onClick={addToCartHandler} className="primary block">
Add to Cart
</button>
</li>
</>
)}
</ul>
</div>
</div>
</div>
);
}
Initialize the Items, Titles and accordion variables only when the product has been set (in the useEffect calls).
You should use a separate state to store the accordion array.
Also, no need to use separate useEffect calls:
let Items = [];
let Titles = [];
const [accordion, setAccordion] = useState([]);
useEffect(() => {
if (product) {
const accordionItems = [product.how_to_use];
accordionItems.unshift(product.ingredients);
accordionItems.unshift(product.longdescription);c
setAccordionItems(accordionItems);
Items = [...accordionItems];
const accordionTitles = [product.how_to_use_title];
accordionTitles.unshift(product.ingredients_title);
accordionTitles.unshift(product.longdescription_title);
setAccordionTitles(accordionTitles);
Titles = [...accordionTitles];
setAccordion([
{ title: product.longdescription_title, body: product.longdescription },
{ title: product.ingredients_title, body: product.ingredients },
{ title: product.how_to_use_title, body: product.how_to_use },
]);
}
}, [product]);

ReactJS error TypeError: Cannot convert undefined or null to object

The affected code is (Typescript errors in local) (componet is correct, the name is as that):
{template.elements.map((element: TemplateElementModel, i) => {
const stand = roomStands?.find(
(stand: ExhibitorModel) => stand.standNumber === `${i + 1}`
);
const componet = stand ? (
<img
src={stand?.logo}
alt={`${stand?.name} logo`}
className="w-24 object-contain"
/>
) : undefined;
const handleClick = stand
? () => {
dispatch(setExhibitor(stand?.id));
dispatch(pushScreen(EventDetailScreen.stand));
}
: undefined;
That's when the code has an error but is just the new registers. When all is ok it shows the view but if has that error the view was a blank page.
I've tried a lot of solutions but i cannot detect what is the problem.
The complete code is:
import React, { useEffect, useState } from "react";
import {
TemplateElementModel,
TemplateModel,
} from "../../template/template.model";
import {
currentEventIdSelector,
roomIdSelector as eventRoomSelector,
popScreen,
pushScreen,
setExhibitor,
setRoom,
} from "../event.slice";
import { useDispatch, useSelector } from "react-redux";
import { Collections } from "../../../config/app.collections";
import DeimosModal from "../../../lib/deimos-ui/components/deimos-modal/DeimosModal";
import { EventDetailScreen } from "../enums/event-detail-screen";
import { ExhibitorModel } from "../../exhibitor/exhibitor.model";
import { RoomModel } from "../../room/room.model";
import { RootState } from "../../../app.store";
import TemplateMarker from "../../template/components/TemplateMarker";
import { TemplateTypes } from "../../template/template-types.enum";
import { useFirestoreConnect } from "react-redux-firebase";
interface EventExhibitionsPageProps {}
const EventExhibitionsPage: React.FC<EventExhibitionsPageProps> = () => {
const dispatch = useDispatch();
const [template, setTemplate] = useState<TemplateModel>();
const [roomStands, setRoomStands] = useState<ExhibitorModel[]>();
const [showRoomsModal, setShowRoomsModal] = useState(true);
const currentRoom = useSelector(eventRoomSelector);
const eventId = useSelector(currentEventIdSelector);
const rooms = useSelector(
(state: RootState) => state.firestore.data[Collections.ROOM]
);
const templates = useSelector(
(state: RootState) => state.firestore.data[Collections.TEMPLATE]
);
const stands = useSelector(
(state: RootState) => state.firestore.data[Collections.EXHIBITOR]
);
useFirestoreConnect([
{
collection: Collections.ROOM,
where: ["event", "==", eventId],
},
{
collection: Collections.TEMPLATE,
where: ["type", "==", TemplateTypes.Room],
},
{
collection: Collections.EXHIBITOR,
where: ["eventId", "==", eventId],
},
]);
useEffect(() => {
if (!currentRoom) return;
const template = templates[currentRoom?.template];
if (template) setTemplate(template);
}, [currentRoom, templates]);
useEffect(() => {
if (!currentRoom) return;
const filteredStands = Object.values<ExhibitorModel>(stands).filter(
(stand) => stand.room === currentRoom.id
);
setRoomStands(filteredStands);
}, [currentRoom, stands]);
return (
<div className="h-full w-full relative bg-no-repeat bg-center text-center">
<DeimosModal
isOpen={showRoomsModal}
hideCancelButton={!currentRoom}
closeButtonText="Salir"
cancelButtonText={`Seguir en ${currentRoom?.name}`}
handleCancel={() => {
setShowRoomsModal(false);
}}
handleClose={() => {
dispatch(popScreen());
}}
>
{!!rooms && (
<div>
<p className="font-bold">Selecciona una sala a la que entrar</p>
{Object.values<RoomModel>(rooms).map((room) => {
return (
<div
className="flex flex-row w-full my-2 p-3 bg-gray-200 rounded cursor-pointer"
onClick={() => {
dispatch(setRoom(room));
setShowRoomsModal(false);
}}
>
{room.name}
</div>
);
})}
</div>
)}
</DeimosModal>
{currentRoom && template && (
<div
className="w-full h-full flex flex-row justify-center align-center"
style={{
backgroundImage: `url(${template.background})`,
backgroundSize: "100% 100%",
}}
>
{template.elements.map((element: TemplateElementModel, i) => {
const stand = roomStands?.find(
(stand: ExhibitorModel) => stand.standNumber === `${i + 1}`
);
const componet = stand ? (
<img
src={stand?.logo}
alt={`${stand?.name} logo`}
className="w-24 object-contain"
/>
) : undefined;
const handleClick = stand
? () => {
dispatch(setExhibitor(stand?.id));
dispatch(pushScreen(EventDetailScreen.stand));
}
: undefined;
return (
<TemplateMarker
// backgroundColor="#327ba8"
width={element.w}
height={element.h}
x={element.x}
y={element.y}
key={element.id}
skewX={element.skewX ?? template.skewX}
skewY={element.skewY ?? template.skewY}
labelComponent={componet}
onClick={handleClick}
/>
);
})}
</div>
)}
</div>
);
};
export default EventExhibitionsPage;
TemplateMarker might not accept labelComponent to be undefined or null. You can add an extra check before sending TemplateMarker.
return componet && (
<TemplateMarker
// backgroundColor="#327ba8"
width={element.w}
height={element.h}
x={element.x}
y={element.y}
key={element.id}
skewX={element.skewX ?? template.skewX}
skewY={element.skewY ?? template.skewY}
labelComponent={componet}
onClick={handleClick}
/>
);

React setState updates value but not showing up in the UI

I'm a beginner and want to write a small warehousing app. A click on the take or add button in the listItem component should call the addAmount or takeAmount functions and change the amount-value of the listItem. console.log provides the right value-change but the number in the UI isn't changing. Can you help me?
App component:
import React, { useState } from 'react';
import logo from './logoblank.svg';
import './App.css';
import { AccessAlarm, AccountBox, LocalCafe, Print } from '#material-ui/icons';
import ListItem from './ListItem/ListItem.js';
import Location from './Location/Location.js';
import Category from './Category/Category.js';
import Search from './Search/Search.js'
const App = props => {
const [locationState, setLocationState] = useState({
locations: [
{ name: '', picture: '' }
]
});
let [listState, setListState] = useState({
listItems: [
{ key: '', icon: <LocalCafe />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 3, orderAction: 'xxx' },
{ key: '', icon: <Print />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 5, orderAction: 'xxx' }
]
});
let [searchState, setSearchState] = useState({value: ''});
console.log(locationState, listState);
const listItems = (
<div>
{listState.listItems.map((listItem, index) => {
return <ListItem
key={index}
icon={listItem.icon}
name={listItem.name}
details={listItem.details}
storage={listItem.storage}
amount={listItem.amount}
click1={() => addAmount(index)}
click2={() => takeAmount(index)}/>
})}
</div>
)
let addAmount = (index) => {
let amount = listState.listItems[index].amount;
amount = amount + 1;
let listCopy = listState;
listCopy.listItems[index].amount = amount;
setListState(listCopy);
console.log(listState.listItems[index].amount);
console.log(listState);
}
let takeAmount = (index) => {
let amount = listState.listItems[index].amount;
amount = amount - 1;
let listCopy = listState;
listCopy.listItems[index].amount = amount;
setListState(listCopy);
console.log(listCopy.listItems[index].amount);
}
let searchChangeHandler = (event) => {
let searchValue = searchState;
searchValue.value = event.target.value;
setSearchState(searchValue);
console.log(searchState);
}
return (
<div className="App">
<header className="App-header">
<div className="App-header-left">
<img src={logo} className="App-logo" alt="SleevesUp!" />
<p className="pad">Warehousing</p>
</div>
<div className="App-header">
<Search
changed={(event) => searchChangeHandler(event)} />
</div>
<div className="App-header-right">
<p className="pad">Roomhero</p>
<AccountBox />
</div>
</header>
<Location
name={locationState.locations[0].name}
picture={locationState.locations[0].picture}
/>
<Category />
{listItems}
</div>
);
}
export default App;
Here is the code of the ListItem.js
import React from 'react';
import './ListItem.css'
const listItem = (props) => {
return (
<div className="listItem">
<div className="listItemColumn icon">
{props.icon}
</div>
<div className="listItemColumn">
<div className="name">{props.name}</div>
<div className="details">{props.details}</div>
</div>
<div className="listItemColumn">
{props.storage}
</div>
<div className="listItemColumnTake">
<button className="take" onClick={props.click2}>Take</button>{props.amount}<button className="take" onClick={props.click1}>Add</button>
</div>
<div className="listItemColumn">
<button className="order">Order</button>
</div>
</div>
)
};
export default listItem;
You are copying the reference of the state to listCopy, hence directly mutating the state.
This won't cause re-render.
Instead create a new object by spreading the current object and it should work fine.
let listCopy = {...listState};
import React, { useState } from 'react';
import logo from './logoblank.svg';
import './App.css';
import { AccessAlarm, AccountBox, LocalCafe, Print } from '#material-ui/icons';
import ListItem from './ListItem/ListItem.js';
import Location from './Location/Location.js';
import Category from './Category/Category.js';
import Search from './Search/Search.js'
const App = props => {
const [locationState, setLocationState] = useState({
locations: [
{ name: '', picture: '' }
]
});
let [listState, setListState] = useState({
listItems: [
{ key: '', icon: <LocalCafe />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 3, orderAction: 'xxx' },
{ key: '', icon: <Print />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 5, orderAction: 'xxx' }
]
});
let [searchState, setSearchState] = useState({value: ''});
console.log(locationState, listState);
const listItems = (list) => (
<div>
{list.listItems.map((listItem, index) => {
return <ListItem
key={index}
icon={listItem.icon}
name={listItem.name}
details={listItem.details}
storage={listItem.storage}
amount={listItem.amount}
click1={() => addAmount(index)}
click2={() => takeAmount(index)}/>
})}
</div>
)
let addAmount = (index) => {
let amount = listState.listItems[index].amount;
amount = amount + 1;
let listCopy = listState;
listCopy.listItems[index].amount = amount;
setListState(listCopy);
console.log(listState.listItems[index].amount);
console.log(listState);
}
let takeAmount = (index) => {
let amount = listState.listItems[index].amount;
amount = amount - 1;
let listCopy = listState;
listCopy.listItems[index].amount = amount;
setListState(listCopy);
console.log(listCopy.listItems[index].amount);
}
let searchChangeHandler = (event) => {
let searchValue = searchState;
searchValue.value = event.target.value;
setSearchState(searchValue);
console.log(searchState);
}
return (
<div className="App">
<header className="App-header">
<div className="App-header-left">
<img src={logo} className="App-logo" alt="SleevesUp!" />
<p className="pad">Warehousing</p>
</div>
<div className="App-header">
<Search
changed={(event) => searchChangeHandler(event)} />
</div>
<div className="App-header-right">
<p className="pad">Roomhero</p>
<AccountBox />
</div>
</header>
<Location
name={locationState.locations[0].name}
picture={locationState.locations[0].picture}
/>
<Category />
{listItems(listState)}
</div>
);
}
export default App;
Try this out. The listitems in the render won't update since it is not directly connected with the listState state. With my change it should re-render when listState changes.

How i can replace states ( arrays ) ReactJs

today I offer you a new challenge
my problem and the next
in the following code
I map objects in my database
I also map a list of items
and so basically I would like to pass my database and not the Items table
basically I want to do exactly the same thing as in the following code except that instead of using the items array, I would like to be able to use the data array which contains my database
do you have any idea how to do this?
I hope I was precise thanks to you for the help Neff
ps: Sorry for the length of the code i try to do my best to clean a little
import React, { Component } from 'react';
import { CardText, Card,Row, Col, Button } from 'reactstrap';
import axios from 'axios'
import GridLayout from 'react-grid-layout';
import SmsForm from './Sms/SMSForm'
import FruitList from './FruitList'
import './AdminPage.scss'
const UP = -1;
const DOWN = 1;
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT+'/api';
class AdminPage extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, name: "orange", bgColor: "#f9cb9c" },
{ id: 2, name: "lemon", bgColor: "#fee599" },
{ id: 3, name: "strawberry", bgColor: "#e06666" }
],
data: [],
}
handleMove = (id, direction) => {
const { items } = this.state;
const position = items.findIndex(i => i.id === id);
if (position < 0) {
throw new Error("Given item not found.");
} else if (
(direction === UP && position === 0) ||
(direction === DOWN && position === items.length - 1)
) {
return; // canot move outside of array
}
const item = items[position]; // save item for later
const newItems = items.filter(i => i.id !== id); // remove item from array
newItems.splice(position + direction, 0, item);
this.setState({ items: newItems });
};
// rest of the component
onHandleChange(event) {
const name = event.target.getAttribute('name');
this.setState({
message: { ...this.state.message, [name]: event.target.value }
});
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
render() {
let datas = this.state.data.map(datass => {
const status = JSON.parse(localStorage.getItem("validated-order") || "{}")[datass.id];
return (
<div>
< Col sm="12" key={datass.id} className="wtfFuHereIsForOnlyBackGroundColorForCol12Nice">
<FruitList fruitList={this.state.items} onMove={this.handleMove} />
<GridLayout className="GridlayoutTextOnlyForGridOuiAndHeigthbecauseHeigthWasBug" layout={layout} cols={12} rowHeight={30} width={1200}>
<div key="a">
<Card body className="yoloCardBodyForbackGroundAndRaduisBorderForAdminPageWtf">
<CardText className="cardTextForAdminPageForDataName"> Commande de {datass.name}</CardText>
</Card>
</div>
</ Col>
</div>
)
})
return (
<div> <div>
<Row className="wtfHereIsAgainOnlyForRowAndMarginForAdminPageJustWtf">
<div className="isJustForOnlyPaddingOnRowForAdminPage" >
<div className="navBarGridMenuAdminPage">
<div className="thatIsOnlyForSpaceArroundOnDivAdminPage">
<CardText className="maybeColForAdminPageOuiOui"> Nouvelles commandes </CardText>
</div>
</div>
<div>
{datas}
</div>
</div>
</Row>
</div>
<div className="box">
</div>
</div>
)
}
}
export default AdminPage
here my second components
import React from "react";
const LEFT = -1;
const RIGHT = 1;
class FruitList extends React.Component {
render() {
const { fruitList, onMove } = this.props;
return (
<div style={{ display: "flex" }}>
{fruitList.map(item => (
<div
key={item.id}
style={{
backgroundColor: item.bgColor,
display: "flex"
}}
>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, LEFT)}>←</a>
</div>
<div className="fruitsId">{item.id}</div>
<div className="fruitsName">{item.name}</div>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, RIGHT)}>→</a>
</div>
</div>
))}
</div>
);
}
}
export default FruitList;
To delete particular list do like this-
pass your item(object).
<a onClick={() => onMove(item)}>→</a>
handleMove function
handleMove = (row) => {
let filtered = this.state.items.filter(item=>item.id!==row.id);
this.setState({ items: filtered});
};

Categories

Resources