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>}
Related
I am calling components as folloews
{userAddresses.map((useraddress, index) => {
return (
<div key={index}>
<Address useraddress={useraddress} />
</div>
);
})}
Their state:
const [showEditAddress, setShowEditAddress] = useState(false);
and this is how I am handling their states
const switchEditAddress = () => {
if (showEditAddress === false) {
setShowEditAddress(true);
} else {
setShowEditAddress(false);
}
};
Well, it's better if you want to toggle between true and false to use the state inside useEffect hook in react.
useEffect will render the component every time and will get into your condition to set the state true or false.
In your case, you can try the following:
useEffect(() => { if (showEditAddress === false) {
setShowEditAddress(true);
} else {
setShowEditAddress(false);
} }, [showEditAddress])
By using useEffect you will be able to reset the boolean as your condition.
Also find the link below to react more about useEffect.
https://reactjs.org/docs/hooks-effect.html
It would be best in my opinion to keep your point of truth in the parent component and you need to figure out what the point of truth should be. If you only want one component to be editing at a time then I would just identify the address you want to edit in the parent component and go from there. It would be best if you gave each address a unique id but you can use the index as well. You could do something like the following:
UserAddress Component
const UserAddress = ({index, editIndex, setEditIndex, userAddress}) => {
return(
<div>
{userAddress}
<button onClick={() => setEditIndex(index)}>Edit</button>
{editIndex === index && <div style={{color: 'green'}}>Your editing {userAddress}</div>}
</div>
)
}
Parent Component
const UserAddresses = () => {
const addresses = ['120 n 10th st', '650 s 41 st', '4456 Birch ave']
const [editIndex, setEditIndex] = useState(null)
return userAddresses.map((userAddress, index) => <UserAddress key={index} index={index} editIndex={editIndex} setEditIndex={setEditIndex} userAddress={userAddress}/>;
}
Since you didn't post the actual components I can only give you example components but this should give you an idea of how to achieve what you want.
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.
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.
I have a Context Consumer which works like this:
<ToastConsumer>
{({ openToast }) => (
<button onClick={() => openToast('You clicked Button A!')}>
Button A
</button>
)}
</ToastConsumer>
But I want to add some extra logic on the click handler and move the openToast Consumer function like this:
upVote = () => {
if (!this.state.hasVoted) {
this.setState({
hasVoted: true,
rating: this.state.rating + 1,
});
this.vote(this.state.rating + 1);
}
this.openToast // not working???
};
<ToastConsumer>
{({ openToast }) => (
<div className="vote-button">
<span
className="vote-up vote-action cursor-pointer"
onClick={this.upVote}
>
👍 +1...
All of the examples provided for the Context API seem to be a simple click handler and I cant workout how to acheive a more complex example like this one.
One way to achieve this is to pass your openToast function into your new handler. You can do this either by wrapping the onClick in a function, or by currying your upVote function.
Examples:
Wrapping in a function:
upVote = (openToast) => {
onClick={() => this.upVote(openToast)}
Currying upVote:
upVote = (openToast) => () => {
onClick={this.upVote(openToast)}
openToast needs to be provided to upVote as an argument (as another answer already mentions), upVote becomes higher-order function:
upVote = openToast => () => {
// ...
openToast();
}
And used like:
<span onClick={this.upVote(openToast)}>
A way to avoid this complexity with context consumers is to make a context available in class instance. This can be done with contextType:
static contextType = ToastContext;
upVote = openToast => () => {
// ...
this.context.openToast();
}
A downside is that this restricts a component to be used with one context.
Or a context can be provided to a component with a HOC:
const withToast = Comp => props => (
<ToastConsumer>
{({ openToast }) => <Comp openToast={openToast} ...props/>}
</ToastConsumer>
);
Then a component that was connected to a context with withToast(MyComponent) receives openToast as a prop:
upVote = openToast => () => {
// ...
this.props.openToast();
}
React onClick event not working when clicking on glyphicon.
const SortableItem = SortableElement(({value, parentId}) =>
<ListGroupItem bsStyle='success' style={{width:'300px',textAlign:'left'}}>
{value}
{parentId === null && <span className='glyphicon glyphicon-remove' style={{float:'right',width:'10px',height:'10px'}}
onClick={e => {e.preventDefault(); console.log('yes')}}/>}
</ListGroupItem>
);
I ran into something similar. My onClick events on my <a> elements were not getting triggered when a user clicked them.
This is what I was doing wrong and maybe you are doing the same mistake as what I was doing. Without more context, it's impossible to diagnose what your actual problem is.
(code is basically saying, when I click the background, stop propagation of the click)
// Example of what I was doing WRONG
const Dialog = ({ onClose, children }) => {
const $overlay = React.useRef(null)
// on mount
React.useEffect(() => {
const handleBackgroundClick = (e) => {
e.stopPropagation() // <<-- This was the line causing the issue
onClose()
})
$overlay.current?.addEventListener('click', handleBackgroundClick)
// on unmount
return () => {
$overlay.current?.removeEventListener('click', handleBackgroundClick)
}
}, [])
return (
<div className="Dialog" ref={$overlay}>{children}</div>
)
}
Basically what I'm saying is this:
Do not use event.stopPropagation() directly on DOM elements in React
The events need to be able to bubble all the way to the top level or in-built React event's will stop working.
I ended up getting around the issue by adding an extra div inside the dialog to act as the overlay.
// How I fixed the issue
const Dialog = ({ onClose, children }) => {
return (
<div className="Dialog">
<div className="Dialog__overlay" onClick={()=> onClose()}></div>
<div className="Dialog__content">{children}</div>
</div>
)
}
Note: These are simplified examples to demonstrate a point. The exact code used was more complex. Using this code as displayed here would cause accessibility issues on your website.