React: Force complete update of component - reactjs

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

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

Jest: functional component array list not mapping after adding element

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

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].

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.

Calling a function for ALL child components

I have 3 components. They parent layout, a select box, and a panel this is generated x times from some data.
<Layout>
<Dropdown>
<Panel>
<Panel>
<Panel>
I'm trying to make it so when the select value changes, the contents of each panel changes. The changes are made by doing some math between the new select value, and data that is stored in the panel component. Each panel has different data.
Layout.js
updateTrueCost(selected){
this.refs.panel.setTrueCost
}
render(){
return (
<div>
<div class="row">
Show me the true cost in
<CurrencyDrop currencyChange = {(e) => this.updateTrueCost(e)} data = {this.state.data} />
</div>
<div class="row">
{this.state.data.map((item, index) => (
<Panel ref="panel" key = {index} paneldata= {item} />
))}
</div>
</div>
);
}
Panel.js
setTrueCost(selected){
//Do some math
this.setState({truecost: mathresult})
}
render(){
return(
<div>
{this.state.truecost}
</div>
)
}
CurrencyDrop.js
onHandelChange(e){
this.props.currencyChange(e);
}
render(){
return(
<Select
onChange={this.onHandelChange.bind(this)}
options={options} />
)
}
The current result is only the last panel updates when the select changes. I'm guessing I'm doing something wrong with the ref handling, but I must not be searching the right terms because I can't find any related questions.
Instead of calling ref's method use React build-in lifecycle methods.
class Panel extends React.Component {
componentWillReceiveProps (newProps) {
// compare old and new data
// make some magic if data updates
if (this.props.panelData !== newProps.panelData) {
this.setState({trueCost: someMath()});
}
}
render () {
return <div>{this.state.trueCost}</div>
}
}
Then just change input props and all data will be updated automatically.

Resources