How to undo OnClick function on second click in ReactJS - reactjs

I have coded a OnClick function that opens a window with a detailed description of the selected object.
However, the window always remains open and to remove it you have to refresh the page. I would like to make sure that at the second click the page returns as before by undoing the function called at the first click.
const setActiveTutorial = (tutorial, index) => {
setCurrentTutorial(tutorial);
setCurrentIndex(index);
};
...
{tutorials &&
tutorials.map((tutorial, index) => (
<TableRow
className={
"list-group-item " + (index === currentIndex ? "active" : "")
}
onClick={() => setActiveTutorial(tutorial, index)}
key={index}
>
<TableCell>{tutorial.title}</TableCell>
<TableCell>{tutorial.size}</TableCell>
<TableCell>{tutorial.country}</TableCell>
...

If you want to use the same function, you can just add a conditional where if currentTutorial already has a value, then it closes the page
const setActiveTutorial = (tutorial, index) => {
if(currentTutorial === tutorial){
setCurrentTutorial(null)
set setCurrentIndex(-1) //or whatever initial value
}
else{
setCurrentTutorial(tutorial);
setCurrentIndex(index);
};
This assumes you can't click on another tutorial while the current one is active.

Related

Antd is not re-rendering the tags in Reactjs

I have a component in reactjs which closes the tag and removes that particular value from the array and show rest of the unclosed tags.
Here's the code for it:
const onHandleCloseTags = (e) => {
// event for onClose for Tags
var array = [...tags]; // make a separate copy of the array
var index = array.indexOf(e);
if (index !== -1) {
array.splice(index, 1);
console.log("slicing: ",array);
setTags(array);
}
}
return(
<>
{tags.map((e, index) => (
<Tag
closable
key={index}
onClose={() => onHandleCloseTags(e)}
>
{e}
{console.count("re-rendering")}
</Tag>
))}
</>
)
If I have these items in tags useState : ['apple','mango'] and closes the apple tag, it automatically also closes the mango tag (more technically antd is making the display of mango tag as hidden).
So how can I overcome this issue? any ideas?
Tried to create separate copy for array but still not working
Try findIndex instead of indexOf , so your function should look like this :
const onHandleCloseTags = (e) => {
var array = [...tags];
var index = array.findIndex(
(item) => item === e
);
if (index !== -1) {
array.splice(index, 1);
console.log("slicing: ",array);
setTags(array);
}
}

React Hooks "useState/useEffect/useCallback" are called conditionally

Please tell me where do I need to put the list.length condition to remove the React Hooks are called conditionally error? I tried to wrap it in useEffect, but in this case an empty list is returned at the first render. It is important that the list is returned at the first render in the same way as with the logic in the code below.
const List = ({ list }) => {
if (list.length === 0) {
return <div>LOADING...</div>;
}
const [localList, setLocalList] = useState(list);
useEffect(() => {
setList(localList);
}, [localList]);
const handleChange = useCallback((id) => {
setLocalList((prevLocalList) =>
prevLocalList.map((item, index) => {
return index !== id ? item : { ...item, checked: !item.checked };
})
);
}, []);
return (
<>
{localList?.map((item, index) => (
<MemoRow key={index} {...item} handleChange={handleChange} />
))}
</>
);
};
The rendered result is returned at the end of the component, not at the beginning. Make that first operation part of the overall return at the end:
return (
list.length === 0 ?
<div>LOADING...</div> :
<>
{localList?.map((item, index) => (
<MemoRow key={index} {...item} handleChange={handleChange} />
))}
</>
);
Additionally, there is a logical issue in your component. When a parent component passes the list value, you are duplicating that in local state in this component. If the parent component changes the value of list, this component will re-render but will not update its local state.
Given the term "LOADING..." in the UI, this implies that's exactly what's happening here. So on a re-render, list.length === 0 is now false, but localList is still empty.
As a "quick fix" you can just update localList any time list changes:
useEffect(() => {
setLocalList(list);
}, [list, setLocalList]);
Of course, this will also over-write any local changes to localList if the parent component ever changes list again. But since this is duplicated state then it's not really clear what should happen in that case anyway. Perhaps you could only conditionally update it if localList is empty:
useEffect(() => {
if (localList.length === 0) {
setLocalList(list);
}
}, [list, setLocalList, localList]);
It's really up to you how you want to handle edge cases like that. But ultimately you're going to need to update localList after list has changed if you want those changes to be reflected in your local state.

Trying to get two functions to run in a single onclick

I am trying to update a number onclick to display 10 more instances when clicking a "load more button", however I am trying to get that button to disappear when the number of instances > the number returned in my array. I have tried to get both of these updates to happen here:
<a className="LoadMoreButton" onClick={() => this.setState({ VisibleNo: this.state.VisibleNo+10})} style={{display: this.state.LoadMoreVis}}>Load More</a>
with the function called being:
allloaded = () => {
if (this.state.data.length < this.state.VisibleNo)
this.setState({LoadMoreVis: 'none'})
}
Is there a simple way to have both of these functions successfully run onclick?
You should use conditional rendering to solve this problem as well as executing both functions on the onClick event.
checkAllLoaded = () => {
if (this.state.data.length < this.state.VisibleNo)
this.setState(prevState => ({...prevState, LoadMoreVis: 'none'}));
}
handleClick = () => {
this.setState(prevState => ({...prevState, VisibleNo: this.state.VisibleNo+10}));
checkAllLoaded();
}
{this.state.LoadMoreVis !== "none" && <a className="LoadMoreButton" onClick={handleClick} style={{display: this.state.LoadMoreVis}}>Load More</a>}

React. TypeError: Cannot read property 'id' of undefined

this is my first question, i'm a newbie dev and i've been working on my first web app for a week. I'm stuck because i'm trying to make a simple mobile e commerce, i've made 8 cards and i've put a plus icon that needs to add the item in the cart when it is clicked, also it has to flip and become a cart icon.
I guess i can't access what it is inside the map method?! and i get the error: TypeError: Cannot read property 'id' of undefined. The problem is inside the some method, I can't come up with any different solution. Any suggestion? Can you help me please? Thanks.
function Mobile() {
const [hovered, setHovered] = useState(false)
const [cartItems, setCartItems] = useState([])
const mobiles = data.map(mobile => (
<div className='card' key={mobile.id}
onMouseEnter = {() => setHovered(true)}
onMouseLeave = {() => setHovered(false)}
>
{cartIcon()}
<img src= {mobile.img} alt='Mobile phones' className='mobile-img' />
<h3 className='mobile-title'>{mobile.title}</h3>
<p className='mobile-info'>{mobile.info}</p>
<p className='mobile-price'>€ {mobile.price}</p>
</div>
))
function addToCart(newItem) {
setCartItems(prevItems => [...prevItems, newItem])
}
function cartIcon() {
const alreadyInCart = cartItems.some(item => item === item.id)
if(alreadyInCart) {
return <i className="ri-shopping-cart-fill cart"></i>
} else if(hovered) {
return <i className="ri-add-circle-line cart" onClick = {() => addToCart()}></i>
}
}
Your function declaration for adding to cart: function addToCart(newItem) expects the item to be passed in as an argument. However, you are passing it nothing:
<i className="ri-add-circle-line cart" onClick = {() => addToCart()}></i>
So when it attempts to add the item to the cart here:
setCartItems(prevItems => [...prevItems, newItem])
newItem is undefined.
Then you attempt to access the property id of undefined and ... error.
cartItems.some(item => item === item.id) // <- item is not an object
Pass in the item you want to add and it will solve your issue. You will have to add it both to the cartIcon call, and its definition, and then pass it to the addToCart after that.
The final result will include something like the following:
{cartIcon(mobile)} // Pass the item
...
function cartIcon(item) { // Expect the item
...
// Add the item
return <i className="ri-add-circle-line cart" onClick = {() => addToCart(item)}></i>
You need to pass item to the cartIcon function. The error is because your function does not have access to whatever item is. I'm assuming it's mobile?
Inside your map pass mobile
{cartIcon(mobile)}
In the cart icon function create an arg called item
function cartIcon(item) {
const alreadyInCart = cartItems.some(item => item === item.id)
if(alreadyInCart) {
return <i className="ri-shopping-cart-fill cart"></i>
} else if(hovered) {
return <i className="ri-add-circle-line cart" onClick = {() => addToCart()}></i>
}
I can't see all the code but this should fix your error.

Unable to display output in React Hooks

I have a list of items and on button click it should display more information on that particular item. For this, I have created one hook that gets the list of all the items from API and displays the list as it iterates inside a <ul> tag. (this is inside the return tag.) On each iteration it shows a button called "Details"
<button onClick ={moreInfo.bind(this, post)} >Details</button>
and when that is clicked it sends the information on that object from the list to the following code -
const moreInfo = (index) => {
console.log(index.name) //works
return ( <div>
{index.map = ( x => (
<div>{x.name}</div>
))}
</div>) //no output
}
console.log is displaying the name but nothing displays via return tag here
You are not mapping thought the array, you are just assigning map property to the index variable
Try do that, like this:
const MoreInfo = (index) => {
return (
<div>
{index.map(x => <div>{x.name}</div>)}
</div>
)
}

Resources