State not updating with useState set method - javascript

I'm trying to learn hooks and try to update the state using onMouseEnter and Leave events, but the state in isFlip.flipStat doesn't change, it used for flag to flipping the card using ReactCardFlip components. The only issues here is my state doesn't change when handleMouse function trigger, maybe anyone can help. Thanks in advance.
Here's my code :
function OurServices() {
const [isFlip, setFlip] = useState([])
const listServices = [
{
"title": "title",
"img": "img.jpg",
"desc": "lorem ipsum"
}
]
function handleMouse(key) {
let newArr = [...isFlip]
newArr[key].flipStat = !newArr[key].flipStat
setFlip(newArr)
}
useEffect(() => {
listServices.map((x) => (
x.flipStat = false
))
setFlip(listServices)
})
return (
{isFlip.map((x, key) => (
<div key={key} onMouseEnter={() => handleMouse(key)} onMouseLeave={() => handleMouse(key)}>
<div className={styles.card} >
<div className={styles.card_body+" p-xl-0 p-lg-0 p-md-1 p-sm-1 p-0"}>
<ReactCardFlip isFlipped={x.flipStat} flipDirection="horizontal">
<div className="row">
<div className={"col-xl-12 text-center "+styles.services_ic}>
<img className="img-fluid" src={x.img} width="72" height="72" alt="data-science" />
</div>
<div className={"col-xl-11 mx-auto text-center mt-4 "+styles.services_desc}>{x.title}</div>
</div>
<div className="row">
<div className={"col-xl-12 text-center "+styles.services_ic}>
{parse(x.desc)}
</div>
</div>
</ReactCardFlip>
</div>
</div>
</div>
))}
)```

The first problem is in your useEffect,
useEffect(() => {
listServices.map((x) => (
x.flipStat = false
))
setFlip(listServices)
})
you are setting listServices as the isFlip array. But Array.map() method doesn't update the source array. You need to write like this,
useEffect(() => {
const updatedArr = listServices.map((x) => (
x.flipStat = false
return x;
))
setFlip(updatedArr)
})
And can you log newArr after this line let newArr = [...isFlip] and see if that array has all the items? It should help you debug the issue.
Update:
Try creating new array while setting the state,
function handleMouse(key) {
let newArr = [...isFlip]
newArr[key].flipStat = !isFlip[key].flipStat
setFlip([...newArr])
}

The main problem is apparently from my code to adding flipsStat key to the listServices variable, to solving this I change the mapping way to this :
listServices.map((x) => ({
...x, flipStat: false
}))
Thanks for #sabbir.alam to reminds me for this.

Related

Passing states between components in React

I am wondering the best way to tackle this problem.
I intend to create a state in the parent component of my application, pass the set state to a component that holds the button (sibling A), and the value to another component where it will be displayed and updated (sibling B). I have a rough idea of how to do this, but ultimately I am lost and am seeking direction. Thank you!
My parent component is as follows
//playlist state
const [playlistItem, setPlaylistItem] = useState()
const mainCards = Data.map(card => {
return(
<MainCard
key={card.id}
id={card.id}
image={card.url}
title={card.title}
playbutton={card.playbutton}
addbutton={card.addbutton}
playlistState={setPlaylistItem}
/>
)
})
const sideCards = SideData.map(card => {
return(
<SideCard
image={card.sideurl}
key={card.id}
title={card.sidetitle}
playbutton={card.playbutton}
addbutton={card.addbutton}
playlistItem={playlistItem}
/>
)
})
return (
<div className="App">
{console.log("main cards" + mainCards[0])}
{console.log("side cards" + sideCards.sidetitle)}
<Navbar />
<Header />
<Playlist />
<CardContainer />
<div className="maincards">
{mainCards}
</div>
<div className="sidecards">
{sideCards}
</div>
</div>
)
}
Sibling A
const handleAdd = (id) => {
console.log(id)
}
return(
<div>
<div className="mainCardObject">
<div className="cardObj">
<img src={props.image} className ="mainCardImage"/>
<img src={props.playbutton} className="playbutton"/>
<img src={props.addbutton} onClick={() => handleAdd(props.id)} className="addbutton" />
</div>
</div>
</div>
)
}
Sibling B
function Playlist(props){
return(
<div className="playlistContainer">
<ul>
<li></li>
</ul>
</div>
)
}
You can pass both getter and setter as parameters, and use them normally in your components:
Sibling A
const handleAdd = (id) => {
//your logic
props.playlistState(prevValue => [])
}
Sibling B
return (
<div className="playlistContainer">
<ul>
<li>
{props.playlistItem}
</li>
</ul>
</div>
)
You can also create a function in your parent component, using set state, and pass this function as a parameter to your component:
Parent
const addPlayListItem = (playListItem) => {
setPlaylistItem(prev => [...prev, playListItem])
}
return (
<SiblingA
addPlayListItem={addPlayListItem}
/>
)
Sibling A
function MainCard(props) {
const handleAdd = () => {
//your logic
props.addPlayListItem({ name: 'rock', title: 'Foo Fighters', year: 2011 })
}
return (
<div>
<img src={props.addbutton} onClick={handleAdd} className="addbutton" />
</div>
)
}
Sibling A:
const handleAdd = (id) => {
props.playlistState((prev) => ([...prev, id]))
}

map in react not displaying data with { } (curly brackets)

So I am trying to return the data from API by map and when I am using ( ) these brackets I am getting the data when I use { } to put if statement, I am getting nothing on my web page but still getting the data in console
const Addtocart = () => {
const data = useSelector((state) => state);
console.log("products", data.productData);
const dispatch = useDispatch();
useEffect(() => {
dispatch(productList());
}, []);
return (
<div id="addtocart-info">
<div className="products">
{data.productData.map((item) => { // here this bracket
if (item.id % 2 === 0 || item.id === 0) {
<div key={item.id} className="product-item">
<img src={item.photo} alt="" />
<div>Name : {item.name} </div>
<div>Color : {item.color} </div>
<button onClick={() => dispatch(addToCart(item))}>
ADD to Cart
</button>
</div>;
console.warn(item.id);
} else {
console.log(item.id);
}
})}
</div>
</div>
);
};
export default Addtocart;
Is there any way to put if statement with () or make this work
You are not getting anything because when u use {} you have to use a return keyword, but when you are using () you don't have to use a return keyword because the whole code inside this is considered as a single piece of code even if it's distributed in multiple lines
so change your code to ,
{data.productData.map((item) => { // here this bracket
if (item.id % 2 === 0 || item.id === 0) {
return (
<div key={item.id} className="product-item">
<img src={item.photo} alt="" />
<div>Name : {item.name} </div>
<div>Color : {item.color} </div>
<button onClick={() => dispatch(addToCart(item))}>
ADD to Cart
</button>
</div>
)
} else {
console.log(item.id);
}
})}
If you use curly brackets you also need to use a return statement. Basically if you don't use curly brackets in an arrow function the statement is returned automatically.
Example:
let x = someArray.map(x => x*2); // returns a new array with the expression applied
let x = someArray.map(x => {return x * 2}) // use the return here

React Useeffect returning null array in first

I making a react project and while data loading i need take my datas from json with fetch-get. But while i doing it its sending empty array in first then 1-2second later sending me datas. So while its empty data my render is starting so my application is crashing. How can i make it without get empty data ? My codes :
const { id } = useParams();
const [articles, setArticles] = useState([]);
const [collection, setCollection] = useState([]);
useEffect(() => {
fetch("http://localhost:3001/api/v1/article/bycollection/" + `${id}`)
.then((x) => x.json())
.then((z) => {
setArticles(z);
}, []);
fetch("http://localhost:3001/api/v1/collection/" + `${id}`)
.then((x) => x.json())
.then((z) => {
setCollection(z);
}, []);
}, [id]);
Empty error line :
return (
<div className="tomiddle ">
<div className="mb-3">
<BreadCrumbComp data={breadCrumbData} />
</div>
<div className="collectionBg">
<div className="collectionHeader flex">
<div className="w-32 h-32 p-4 mt-2 text-5xl">
<i className={collection.icon}></i>
</div>
<div>
<h1 className="collectionTitle ">{collection.name}</h1>
<WriterGroupInfo groupData={collection} /> // Here i getting error.
Its sending empty collection
</div>
</div>)
WriterGroupInfo :
const WriterGroupInfo = (props) => {
const prop = props.groupData;
console.log("prop=>", props); // Showing empty array.
const articles = prop.articles.slice(0, 3);
const last = articles[articles.length - 1];
const lastAuthor = articles.length > 1 ? " ve " + last.author.name : "";
const authorNames =
articles
.slice(0, 2)
.map((a) => a.author.name)
.join(", ") + lastAuthor;
return (
<div className="flex pt-4 ">
<Avatar.Group>
{articles.map((x) => (
<Avatar key={x.id} src={x.author.avatar} />
))}
</Avatar.Group>
<div className="text-collection-small-500 truncate text-xs pl-2 pt-1">
<span>Bu koleksiyonda {prop.articles.length} makale mevcut</span>
<br />
{articles.length > 0 ? (
<span className="truncate mr-2">
<span className="text-gray-400 ">Yazarlar: </span>
{authorNames}
</span>
) : (
<></>
)}
</div>
</div>
);
};
This is happening because you are setting the first value of collecion as an empty array:
const [collection, setCollection] = useState([]);
You should handle this situations like this:
return (
<div className="tomiddle ">
<div className="mb-3">
<BreadCrumbComp data={breadCrumbData} />
</div>
{collection.length === 0 ?
<div> No data for collection </div> :
<div className="collectionBg">
<div className="collectionHeader flex">
<div className="w-32 h-32 p-4 mt-2 text-5xl">
<i className={collection.icon}></i>
</div>
<div>
<h1 className="collectionTitle ">{collection.name}</h1>
<WriterGroupInfo groupData={collection} /> // Here i getting error.
Its sending empty collection
</div>
}
</div>)
Your callback inside useEffect has 2 async calls and you are not waiting for them to finish. They take their time. Before that the next render has already taken place and for that reason you have your data empty for some time until your fetch(and subsequent setState) is completed.
Since both fetch are independent you can wait for them using Promise.all and await.
useEffect( () => {
async yourFunction() {
let promise1 = fetch("http://localhost:3001/api/v1/article/bycollection/" + `${id}`).then((x) => x.json()).then((z) => {
setArticles(z);
});
let promise2 = fetch("http://localhost:3001/api/v1/collection/" + `${id}`).then((x) => x.json())
.then((z) => {
setCollection(z);
});
let results = await Promise.all([promise1,promise2]);
}
yourFunction();
}, [id]);
Also, [] as an extra parameter for the second then() is not required in my opinion.
The issue is UseEffect works asynchronously and it triggers after the initial render. So at the initial render, it tries to populate using null/Empty array values. You can initiate the passing array values using useState like const[value,setValue]=useState([]) .

iterating through an array of objects and displaying the items [REACT JS]

I'm trying to iterate through an array of objects, displaying the results inside divs but something is not working as intended. When I console log it seems to retrieve the data and show it.
const example =
{
"example": [
{
"test": "test",
"img": "img.png",
"song": "song title"
},
{
"test": "test2",
"img": "img.png2",
"song": "song title2"
}
]
}
const renderData= () => {
example.example.forEach(function (arrayItem) {
const test= arrayItem.test
const img= arrayItem.img
const song= arrayItem.song
return (
<div className="test">
<div className="test">
<div className="test">
<img
src={img}
alt="sunil"
/>
</div>
<div className="test">
{test}
<span className="test">
</span>
<p>{song}</p>
</div>
</div>
</div>
);
});
};
return (
<div
{renderData()}
</div>
);
}
nothing really shows up, but when i do:
example.example.forEach(function (arrayItem) {
var x = arrayItem.test+ arrayItem.img+ arrayItem.song;
console.log(x);
});
it works and consoles the right info.
Can anyone spot the mistake or help out?
Please ignore the naming convention.
You need return array of JSX.Element from renderData. In your case you return undefined. Return a new array of JSX.Element with map instead forEach, which returns nothing.
const renderData = () => {
return example.example.map((arrayItem, i) => {
const test = arrayItem.test;
const img = arrayItem.img;
const song = arrayItem.song;
return (
<div key={i} className="test">
<div className="test">
<div className="test">
<img src={img} alt="sunil" />
</div>
<div className="test">
{test}
<span className="test"></span>
<p>{song}</p>
</div>
</div>
</div>
);
});
};

Curly braces inside curly braces in React

I have presentational component in React. And with products.some i am trying to check if any item inside products is checked. And if some item is checked, render parent block for RequestedProduct component. I know that the problem is a second pair of curly braces as React think it's a prop. Is there another way to do this?
const Requested = ({ products, getCurrentTime }) => (
<div className="pepper-pin-body-tab requested-tab">
<div className="pepper-pin-body-tab-title">
Запрошенные
</div>
<div className="pepper-pin-body-tab-indicator" />
{products.some(product => product.checked) ? (
<div className="requested-tab-list-requested">
<div className="requested-tab-list-requested-time">
{getCurrentTime()}
</div>
{products.filter((product, key) => {
if (product.checked) {
return (
<RequestedProduct
key={key}
title={product.title}
/>
);
}
})}
</div>
) : null}
</div>
);
Issue is, filter will not return the custom element/value, it will always return the array element for which you return true from filter body.
Solution is, use only map or combination of filter and map.
Using map:
{
products.map((product, key) => product.checked ?
<RequestedProduct key={key} title={product.title} />
: null
}
Using combination of filter and map:
{
products
.filter(product => product.checked)
.map((product, key) => <RequestedProduct key={key} title={product.title}/>)
}
Check this snippet, you will get a better idea:
const arr = [
{a: 1},
{a: 2},
{a: 3},
{a: 4}
];
const afterFilter = arr.filter((el,i) => {
if(i%2) {
return `Hello world ${i}`;
}
});
// it will print the array items, not the Hello World string
console.log('afterFilter', afterFilter);
I'd recomment splitting the code a bit, which makes it intent a lot clearer. You'll end up with the following (for example), which should not be triggering errors.
The main problem is in the unintended side effects of the filter, whereas you most likely want to use a filter and a map. That makes the intent to another developer much clearer.
const contents = (products, getCurrentTime) => (
const filtered = products.filter(product => product.checked);
<div className="requested-tab-list-requested">
<div className="requested-tab-list-requested-time">
{getCurrentTime()}
</div>
{filtered.map((product, key) => <RequestedProduct key={key} title={product.title}/>)}
</div>
);
const Requested = ({products, getCurrentTime}) => {
const isAnythingChecked = products.some(product => product.checked);
return <div className="pepper-pin-body-tab requested-tab">
<div className="pepper-pin-body-tab-title">
Запрошенные
</div>
<div className="pepper-pin-body-tab-indicator"/>
{isAnythingChecked ? contents(products, getCurrentTime) : null}
</div>
};

Categories

Resources