Jest: functional component array list not mapping after adding element - reactjs

I am trying to list person by clicking onClick function to add empty object so that map can loop
over and show div. I see prop onclick function calling works but i map is not looping over it.
a little help would be appreciated.
// functional component
const listing = ({list=[]}) => (
<>
{
<div id='addPerson' onClick={() => list.push({})}>Add person</div>}
{list.map((element, index) => (
<div key={index} className='listItems'>
<p>list name</p>
</div>
))}
</>
);
export default listing;
// test case
it('renders list-items', () => {
const wrapper = shallow(<listing />);
wrapper.find('#addPerson').prop('onClick')();
console.log(wrapper.find('.listItems').length); // showing zero, expected 1
});

Updating List is not going to cause a re-render in your component. I am not sure where List is coming from, but I am assuming it is coming from a local state in the parent, or probably redux store. From inside your component, you will have to call a function in your parent to update the list array there.
Below is a version of your component, assuming the list in your local state. You will have to adjust the code to update the state in the parent or redux store, but the below should be enough to give you an idea of what to do. Also, don't push the element to array, as it mutates the object and will not contact re-render.
const Listing = ({list = [], addToList}) => {
return <>
{
<div id='addPerson' onClick={() => addToList({})}>Add person</div>}
{list.map((element, index) => (
<div key={index} className='listItems'>
<p>list name</p>
</div>
))}
</>
};

Related

UseEffect does not rerender when remove data out of an array only upon adding data does it rerender

import React, { useEffect, useState } from 'react'
import { HiOutlineBackspace } from "react-icons/hi";
import {MdOutlineCancel} from 'react-icons/md'
const Cart = ({toggleCart}) => {
const localStorageCart = JSON.parse(localStorage.getItem('MY_CART')) || []
const [cartItem, setCartItem] = useState(localStorageCart)
*************************************
const handleDeleteButton = (index) => {
cartItem.filter(cartItem[index])
console.log(cartItem);
}
**************************************
useEffect(() =>{
setCartItem(cartItem)
},[cartItem])
return (
<div className='cartContainer'>
<h1 style={{textAlign:"center"}}>Your Cart</h1>
<button
style={{backgroundColor:'transparent'}}
onClick={toggleCart}
>
<HiOutlineBackspace/>
</button>
<div className='cartCardContainer'>
{cartItem.map((cartItem,index) =>{
return(
<div className="cartCard" key={index}>
<img src={cartItem.img} alt="logo" id="img" />
<h2>{cartItem.title}</h2>
<div className="description">
<h5>{cartItem.description}</h5>
</div>
<div className="priceCard" >
<h3>${cartItem.price}</h3>
</div>
<button onClick={() => handleDeleteButton(index)}>***********************
<MdOutlineCancel/>
</button>
</div>
)
})}
</div>
</div>
)
}
export default Cart
I am working on an e-commerce add to Cart function.
By using useEffect I managed to rerender the cart right upon adding an item since it was a one-page app.
But when I tried to splice( ) the cartItem I heard that it is not a good practice and that I should use filter( ) instead but I am having trouble doing so as I have tried many ways and the filter doesn't go as planned or it just dont run.
For this part of the project I use a lot of Local Storage as it is the addToCart component
I have put * symbol on where I think the problem might be
The filtering is not implemented correctly. Also, you have to update the state after filtering/removing items from the state.
You can use splice() to remove an element from an array.
The following is how handleDeleteButton should look like:
const handleDeleteButton = (index) => {
cartItem.splice(index, 1)); // 1 means remove one item only
setCartItem([...cartItem]);
}
The filter method returns a new array based on the condition you provide it. So in your code you're not actually altering the state of your cartItem.
Filter your cartItem array and use the return value to set your cartItem state.
const handleDeleteButton = (index) => {
setCartItem(cartItem.filter(...));
}
As a side note, you would be better filtering your items by a key or identifier associated with the item rather than the index value of your map function.
useEffect woke when you are updating the state your state now is cartItem,
To update this state most be execute setCartItem well in your function well you must do this :
setCartItem(cartItem.splice(cartItem[index],1)
I do not know what is inside your array cartItem well do you filter and change it with setCartItem

When I set state via useState in parent component, my props in the child I'm passing down to are undefined

I have a parent component Listings, that has an array of objects from a seed file. I only want to load the first 9 of 14 objects on page load.
const Listings = () => {
const [ listingsData, setListings] = useState(listings);
const [ homesDisplayed , setDisplayNumber ] = useState(listingsData.slice(0, 9));
const homes = homesDisplayed.map((home, index) => (
<div className="col xl4" key={index}>
<HomeListingCard
name={home.homeName}
imageUrl={home.imageURL}
beds={home.beds}
baths={home.baths}
isMultiSection={home.isMultiSection}
sqft={home.sqft}
startingPrice={home.startingPrice}
/>
</div>
));
return (
<div>
<div className="row">
<div className="listings-subheader">
<h3 className="left available-homes">{`${listingsData.length} `}homes available</h3>
<div className="right">
<SortBy />
</div>
</div>
</div>
<div className="row">
{homes}
</div>
<div className="row">
<LoadListings listings={listingsData} homesDisplayed={homes} setDisplayNumber={setDisplayNumber} />
</div>
</div>
)
}
export default Listings;
I use const [ homesDisplayed , setDisplayNumber ] = useState(listingsData.slice(0, 9)); to set an array of 9 objects to state.
This works fine.
Then I am trying to pass my state down to a child component as props like this:
<LoadListings listings={listingsData} homesDisplayed={homes} setDisplayNumber={setDisplayNumber} />
When the page initially loads, those props have data. But when I click a button in the <LoadListings /> child component to console.log(props), my data is all undefined.
<LoadListings /> looks like:
const LoadListings = (props) => {
const updateListings = (props) => {
console.log(props.homes);
// props.setDisplayNumber(...props.homesDisplayed, props.listings.slice(9, 12));
}
return (
<button onClick={updateListings}>Load More</button>
)
}
My goal is to click the button and have updateLIstings splice 3 more off the original array of 14 and add them to homesDisplayed array like so
const updateListings = (props) => {
props.setDisplayNumber(...props.homesDisplayed, props.listings.slice(9, 12));
}
but props.homesDisplayed -- and all props -- are undefined.
Thank you.
This is happening because you're not actually passing your props down to the updateListings function.
If you have a closer look at your function declaration, you'll notice that you've defined props as a parameter. Then, on the onClick attribute, you're not actually passing anything to the function.
Think about the scope of the updateListings function... You don't actually need to pass it anything, as it already has access to props from the parent scope. Unfortunately, by defining props a second time as a function parameter, you're shadowing the higher-scope props (effectively telling the function to ignore it).
If you update updateListings to omit the props argument, it should work (barring any other problems in your code).
const updateListings = (props) => {
console.log(props.homes);
props.setDisplayNumber(...props.homesDisplayed, props.listings.slice(9, 12));
}
Side note: Instead of storing the sliced data in state, you may want to just store the number of items you intend to show. That way, instead of reasoning about the data in state, you can simplify everything by just performing the slice in the render itself. For example:
const [ listingsData, setListings] = useState(listings);
// Store number to display
const [ homesDisplayed , setDisplayNumber ] = useState(9);
// Slice before map, which'll update every time you increment `homesDisplayed
const homes = homesDisplayed.slice(0, homesDisplayed).map((home, index) => (
<div className="col xl4" key={index}>
<HomeListingCard
name={home.homeName}
imageUrl={home.imageURL}
beds={home.beds}
baths={home.baths}
isMultiSection={home.isMultiSection}
sqft={home.sqft}
startingPrice={home.startingPrice}
/>
</div>
));
Another side note: typically you'll want to name your [state, setState] val/updater consistently following the convention of varName, setVarName. That way it's less confusing to the reader (and you!) later on when you're trying to reconcile what controls what.
So I'd suggest changing [homesDisplayed, setDisplayNumber] to [displayNumber, setDisplayNumber].

React not rendering all elements within a list of components

I have a system in which when the user presses a button, a new component is pushed to a list of components and the state is updated. I render the list of components using {}, but only the first element is rendered.
I've used console.log to ensure that my list is actually updating. All the questions I've seen so far for this problem involve using a class that extends React.Component. Since I'm using a function to render, I don't see how I can use those solutions
export default function ExampleManager() {
const [examples, setExamples] = React.useState([<Example key={0}/>);
function handleClick() {
examples.push(<Example key={examples.length}/>);
setExamples(examples);
}
return (
<>
{examples}
<Button variant="outlined" color="primary" onClick={handleClick}>
Add Example
</Button>
</>
);
}
If the button was to be clicked multiple times, I would expect there to be multiple Example components, however at the moment only the first element works
As far as I know, examples is immutable and isn't updated by using examples.push().
Change your handleClick to the following code, to remove the reference of your example variable:
function handleClick() {
// create a new array to prevent referencing the old on
setExamples([
...examples,
<Example key={examples.length}/>
]);
}
Nonetheless you shouldn't add components per se into your array. Try to split values and its representation like the following:
function ExampleManager() {
const [examples, setExamples] = React.useState([0]);
const handleClick = () => setExamples([...examples, examples.length])
return (
<>
{examples.map((item, key) => <Example key={key} data={item} />)}
<Button variant="outlined" color="primary" onClick={handleClick}>
Add Example
</Button>
</>
)
}

React: Force complete update of component

I have a component that I have written (it is a list). When the component is updated (this list is changed), the onclick event does not update and passes the same values as before the component was rendered. Is there a way to force React to update the entire div from scratch? The variable "value.edit_title_normal" does change, just not the onclick event.
*I have removed some excess code for brevity *
class SearchResults extends React.PureComponent {
//Code to show the detailed information
render() {
//Individual record
const startItem = (this.props.itemsPerPage * this.props.page) - this.props.itemsPerPage;
const endItem = startItem + this.props.itemsPerPage;
//Slice is from/to
let all = searchableDatabase.slice(startItem, endItem).map((value, index) => {
return (
<div onClick={(e) => this.showDetailedInfo(index, e)} > // <== This bit here is not updating when re-rendered
{value.edit_title_normal}
</div>
)
});
//Main container
return (
<div>
<div>
<Pagination allLength={searchableDatabase.length} page={this.props.page} itemsPerPage={this.props.itemsPerPage} />
</div>
<div>
{all}
</div>
</div>
);
}
}
You've misdiagnosed the problem. The index in the map callback always starts from zero, so it's impossible for it to ever change to something else. You're operating on a new array sliced from the original and the map method doesn't know or care about the original array's indexes.
I assume that you expect the index to start from startItem instead of 0. Then you'll have to add it manually to the index:
<div onClick={(e) => this.showDetailedInfo(index + startItem, e)} >

Reactjs not updating dom classes after splicing state

Hello I am new to react and have a question with changing classes and animation onClick.
I am trying to move up and down only with css classes that I add or remove to an array that is in my className.
app.js i have this in state
updown: ["containerMG"],
and here is how i render my components in app.js
render() {
return (
<div>
<div className="movies-container">
{this.state.filmovi.map((film, index) => {
return <Film
naslov={film.naslov}
naslovnaSlika={film.naslovnaSlika}
key={film.id}
openFilm={() => this.injectFilm(index)}/>
})}
</div>
<Gallery />
<ContainerMG
selectedFilm={this.state.selectedFilm}
klasa={this.state.updown}
zatvori={this.closePreview}/>
</div>
);
}
my component looks like this
const ContainerMG = (props) => {
return (
<div className={props.klasa.join(' ')}>
<img onClick={props.zatvori} src="xxx" alt="close" className="close-popup" />
<p>{props.selectedFilm.naslovFilma}</p>
</div>
)
}
this is how the div moves up
injectFilm = (filmIndex) => {
this.state.updown.push("position-top");
const selectedFilm = this.state.filmovi.find((film, index) => index === filmIndex)
this.setState((prevState) => ({
selectedFilm
}))
}
this is how i tried to move it down
closePreview = () => {
this.state.updown.splice(1);
}
i am pretty sure i should not change the state directly, and also when i change it to remove the "position-top" class the dom doesn't reload.
thank you for all the help in advance and if i didn't show something that you need please do write.
You're right, you should never change the state directly like that, but rather, use a setState() method. Doing this.state.updown.push("position-top"); will mutate it. Let's say you want to remove whatever is last from the array, you could do something like:
const {updown} = this.state;
updown.pop();
this.setState({updown: updown});
Which would cause a re-render. Treat the state as if it were immutable.

Resources