I want to make the "ADD" button to " ADDED" after I clicked the ADD button and make it disabled.
{
products.map((val, i)=>(
<div className="product_item">
{
val.productType === 'chicken'?
<>
<img src={img2}></img>
<p className="product_data">Chicken Name: {val.chickeName}</p>
<p align="left" className="product_data">weight: 500gm</p>
<p align="left"className="product_data">₹{val.price}/- <button onClick={addTocartProduct}>ADD</button></p>
</>: null
}
</div>
))
}
I have sample project in react js having add to cart button is there. But I want to make the "ADD" button to " ADDED" after I clicked the ADD button and make it disabled.
If anyone can know please, drop your answer.
Considering that you have an array of products you should initialize an array of bool:
const [buttonsClicked, setButtonsClicked] = useState(new Array(products).fill(false));
Then
{
products.map((val, i)=>(
<div className="product_item">
{
val.productType === 'chicken'?
<>
<img src={img2}></img>
<p className="product_data">Chicken Name: {val.chickeName}</p>
<p align="left" className="product_data">weight: 500gm</p>
<p align="left"className="product_data">₹{val.price}/- <button onClick={(e) => addTocartProduct(val, i)}>{buttonsClicked[i] ? 'ADDED' : 'ADD'}</button></p>
</>: null
}
</div>
))
}
And finally the addTocartProduct function:
const addTocartProduct = (val , i) => {
let result = [...buttonsClicked];
result[i] = !result[i];
setButtonsClicked(result);
}
Youjust need a simple ternary condition and a state. Here is an example :
const App = () => {
const [isAdded, setIsAdded] = React.useState(false);
const handleClick = () => {
setIsAdded(true);
}
return (
<div>
<button onClick={handleClick}>{isAdded ? 'ADDED' : 'ADD'}</button>
</div>
);
};
You have to define some state in your component
const [ctaButton,setCtaButton] = useState('ADD')
Then in your addTocartProduct function set the state to new one
setCtaButton('ADDED')
Finally
<button onClick={addTocartProduct}>{ctaButton}</button>
Related
I am trying to figure out on how to set a button that can independently be disabled when using the .map function. So I created a state whenever the button is clicked, it disables that specific button. However, it only disables all of the buttons which is not what i wanted. Is there a way to make the buttons have their own click event which can be disabled according to the index?
const [click, setClick] = useState(false);
const array = ["item 1", "item 2", "item 3"];
const disableClick = () => {
setClick(true);
};
return (
<div className="App">
{array.map((item) => (
<div>
<h2>{item}</h2>
{!click ? (
<button onClick={disableClick}>CLICK {item}</button>
) : (
<button disabled>Button clicked</button>
)}
</div>
))}
</div>
);
Move the click handler and useState in a separate component.
const ButtonView = (textContent) => {
const [disabled, setDisabled] = useState(false);
const onClick = () => {
setDisabled(true);
};
if (disabled) {
return <button disabled={disabled}>Button clicked</button>;
}
return <button onClick={onClick}>CLICK {textContent}</button>;
};
export const View = () => {
const array = ["item 1", "item 2", "item 3"];
return (
<div className="App">
{array.map((item, key) => (
<div key={key}>
<h2>{item}</h2>
<ButtonView textContent={item} />
</div>
))}
</div>
);
};
Or do this:
const ButtonView = (textContent) => {
const [disabled, setDisabled] = useState(false);
const onClick = () => {
setDisabled(true);
};
const content = disabled ? "Button clicked" : textContent
return <button disabled={disabled} onClick={onClick}>CLICK {content}</button>;
};
Disable takes an arguments you can supply it with
<button disabled={click? true:false} >Button clicked</button>
Is there a way to make the buttons have their own click event which
can be disabled according to the index?
Yes, How? instead of setting the click state to true or false, we can set it to contain name of the button const [disabledBtn, setDisabledBtn] = useState("item 1"); and then we can compare it with the items from the map.
<button disabled={disabledBtn == item ? true:false} >Button clicked</button>
This code render the buttons with the first button disabled by default, if the user clicks on one of the other available buttons it will trigger rerender and that btn will be disabled.
You should add a key to your childern components to maintain uniqueness between renderings.
This code snippet should answer your question
const [disabledBtn, setDisabledBtn] = useState("item 1");
const array = ["item 1", "item 2", "item 3"];
const handleDisableClick = (item) => {
setDisabledBtn(item);
};
return (
<div className="App">
{array.map((item, idx) => (
<div key={idx}>
<h2>{item}</h2>
<button
disabled={disabledBtn == item ? true : false}
onClick={() => handleDisableClick(item)}
>
Button clicked
</button>
</div>
))}
</div>
);
In React component I have array of books, on click of Hide Books I set array of data to empty so nothing is displayed on page the change Button text to Show Books
What I am trying to achieve is When click on button with text Show Books I want to display data by setting setBooksData(bookData)
function BookList() {
const [booksData, setBooksData] = useState(books);
const clearBooks = () => {
setBooksData([]);
};
return (
<section className="booklist">
{booksData.map((book, index) => {
return <Book key={index} {...book}></Book>;
})}
<button onClick={clearBooks}>
{booksData.length === 0 ? "Show Books" : "Hide Books"}
</button>
</section>
);
}
We can use a another state to hide and show the book list. In your code you have made booklist as empty, so that when you click on the show book, booklist will be empty.
Now here i have used another state to hide and show the books.
to show the book i have added two condition to check whether the book array has data and whether in show state.
function BookList() {
const [booksData, setBooksData] = useState(books);
const [hideBook,setHidden]=useState(false);
const clearBooks = () => {
setHidden(!hideBook)
};
return (
<section className="booklist">
{!hideBook && booksData.length >0 &&
booksData.map((book, index) => {
return <Book key={index} {...book}></Book>;
})
}
<button onClick={clearBooks}>
{hideBook ? "Show Books" : "Hide Books"}
</button>
</section>
);
}
with you logic, you need to toggle the item, if it have list and clicking on hide it will hide otherwise it will reset
const BookLists = () => {
const [bookLists, setBooksData] = useState(books);
const toggleBookList = () => {
if (!!bookLists.length) {
setBooksData([]);
return;
}
setBooksData(books);
};
return (
<section className="booklist">
<ul>
{bookLists.map((book, index) => (
<Book key={index} label={book} />
))}
</ul>
<button onClick={toggleBookList}>
{bookLists.length === 0 ? 'Show Books' : 'Hide Books'}
</button>
</section>
);
};
export { BookLists };
Since both buttons have different functionality, you can separate them, and assign each its own handler functions
function BookList() {
const [booksData, setBooksData] = useState(books);
const clearBooks = () => {
setBooksData([]);
};
const handleShowBook = () => {
setBooksData(books);
}
return (
<section className="booklist">
{booksData.map((book, index) => {
return <Book key={index} {...book}></Book>;
})}
{
booksData.length === 0
?
(<button onClick={handleShowBook}>
Show Books
</button>)
:
(<button onClick={clearBooks}>
Hide Books
</button>)
}
</section>
);
}
I have a component in React where when the "Add" button is clicked, new form fields are created. However, when I click on the add button, the new form fields are added at the top of the add button instead of below it.
I need the add button to be fixed at the top and the new form fields created to stack at the top of the other existing form fields. I would like to know what I need to change on my code to achieve this.
function DynamicFormFields() {
const [inputList, setInputList] = useState([{ name: "", quantity: "" }]);
// handle input change
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
// handle click event of the Remove button
const handleRemoveClick = (index) => {
const list = [...inputList];
list.splice(index, 1);
setInputList(list);
};
// handle click event of the Add button
const handleAddClick = () => {
setInputList([{ name: "", quantity: "" }, ...inputList]);
};
return (
<>
<div className="DynamicFormFields">
<h3>
<p>Dynamic Form</p>
</h3>
{inputList.map((x, i) => {
// x -> element, i -> index
return (
<div className="box">
<div className="btn-box">
{inputList.length - 1 === i && (
<button onClick={handleAddClick}>Add</button>
)}
</div>
<input
name="name"
placeholder="Name"
value={x.name}
onChange={(e) => handleInputChange(e, i)}
/>
<input
type="number"
name="quantity"
// className="ml10"
min="1"
max="12"
></input>
{inputList.length !== 1 && (
<button className="mr10" onClick={() => handleRemoveClick(i)}>
Remove
</button>
)}
</div>
);
})}
</div>
</>
);
}
Ok, I'm not sure I completely understand what you want but I made some changes real quick (may contain mistakes). Is this what you're trying to do? Also I would suggest making your input items into small sub components, that may help you see the overall picture more clearly.
function DynamicFormFields() {
const [inputList, setInputList] = useState([{ name: "", quantity: "" }]);
// handle input change
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
// handle click event of the Remove button
const handleRemoveClick = (index) => {
const list = [...inputList];
list.splice(index, 1);
setInputList(list);
};
// handle click event of the Add button
const handleAddClick = () => {
setInputList([{ name: "", quantity: "" }, ...inputList]);
};
return (
<>
<div className="DynamicFormFields">
<h3>
<p>Dynamic Form</p>
</h3>
<div className="box">
<div className="btn-box">
<button onClick={handleAddClick}>Add</button>
</div>
<div>
{inputList.map((x, i) => {
// x -> element, i -> index
return (
<div>
<input
name="name"
placeholder="Name"
value={x.name}
onChange={(e) => handleInputChange(e, i)}
/>
<input
type="number"
name="quantity"
// className="ml10"
min="1"
max="12"
></input>
{inputList.length !== 1 && (
<button
className="mr10"
onClick={() => handleRemoveClick(i)}
>
Remove
</button>
)}
</div>
);
})}
</div>
</div>
</div>
</>
);
}
The main changes here is that I put the button outside of the loop as it shouldn't be dependent on your other input items, as I understand it. I also wrapped your inputs inside another div to completely separate the button and the inputs. Make sure only what needs to be updated gets updated so you don't get unexpected behaviour.
I want to make a button check which add a new calssName to my list. I use a function to update a state and take the string. If you want to help me be more specific because I am a beginner. Thanks !
const [check, setCheck] = useState({
id: '',
status: false
});
This is the function. With 'on' I take the string to add to id.
let complet = (on) =>{
if(check.status == false){
setCheck({
id: on,
status: true
})
}
else {
setCheck({
id: on,
status: false
})
}
}
And how I Display the list and how I check correspond to the string.
return(
<div className='display'>
{ list.map( (list,index) => (
<div className={ check.status && check.id == list ? 'lista complet' : 'lista'} key= {index} id='lista' >
{list}
<button className='btnCheck' onClick={complet.bind(this, list)}> <FcCheckmark/> </button>
<button className='btnRemove' onClick={remove.bind(null, list)}> <BsTrash/> </button>
</div>
))}
</div>
)
If you want to store the checked ids and the unchecked ids, you must change your state variable because currently it can only stores a single element. However, it seems you are rendering a list of elements that can be checked individually
Here is a possible solution :
function App({list}) {
const [checkIds, setCheckIds] = useState(() => {
const item = localStorage.getItem('checkIds');
return item ? JSON.parse(item) : {};
});
// reset the checkIds when the list is updated
useEffect(() => setCheckIds({}), [list]);
// save the checkIds into the local storage
useEffect(() => {
localStorage.setItem('checkIds', JSON.stringify(checkIds));
}, [checkIds]);
function checkId(id) {
setCheckIds({...checkIds, [id]: true);
}
function uncheckId(id) {
setCheckIds({...checkIds, [id]: false);
}
return (
<div className='display'>
{list.map(id => (
<div key={id} id={id} className={check[id] ? 'lista complet' : 'lista'}>
{id}
<button className='btnCheck' onClick={() => checkId(id)}>
<FcCheckmark/>
</button>
<button className='btnRemove' onClick={() => uncheckId(id)}>
<BsTrash/>
</button>
</div>
))}
</div>
)
}
const Chat = () => {
const bodyRef = useRef(null)
const [{messages,roomMessages,roomID,socket,user}, dispatch] = useStateValue()
const [dropdown, setDropdown] = useState(false)
useEffect(()=>{
const setScreen = () =>{
bodyRef.current.scrollTop = bodyRef.current.scrollHeight
}
setScreen()
},[])
const updateMessages = (message) =>{
const allMessages = messages
allMessages.push(message)
dispatch({
type: "SET_MESSAGES",
item: allMessages
})
bodyRef.current.scrollTop = bodyRef.current.scrollHeight
}
useEffect(() => {
socket.on('new-message', (message) =>{
if(messages === null){
console.log('here it was null')
} else {
updateMessages(message)
}
})
// clean up socket on unmount
}, [])
const handleDropdown = (index) =>{
setDropdown(true)
console.log(index)
if(dropdown === true){
setDropdown(false)
}
}
return (
<div className={'chat'}>
<ChatHeader/>
<div ref={bodyRef} className={'chat__body'}>
{messages?.map((item,index)=>(
<Emojify key={item._id}>
<div
onMouseEnter={() => handleDropdown(index)}
onMouseLeave={handleDropdown}
className={`chat__message ${item.name === user.displayName && "chat__reciever"}`}>
<h5 className={'chat__messageUser'}>
{item.name}
</h5>
<p className={'chat__messagetext'}>
{item.message}
</p>
{dropdown && <Button><ArrowDropDownIcon/></Button>}
{item.img && <img alt={''} className={'chat__img'} src={item.imgURL}/>}
</div>
</Emojify>
))}
</div>
<div className={'chat__footer'}>
<ChatFooter/>
</div>
</div>
);
I want to render the Button {dropdown && <Button><ArrowDropDownIcon/></Button>} when it is hovered over.
Right now if I hover over one div it gets rendered for all other mapped divs but I don't want this; it shoul be for a specific div only.
How would I do this? Does someone have an idea? I passed index but can't seem to use it as I desire.
Instead of storing a single "global" boolean for dropdown open or not (which all mapped elements are reading), you could correlate a specific dropdown you want to be open. It's good you are already passed an index to the handler.
Change state to store null (no dropdown) or an index (show dropdown).
const [dropdown, setDropdown] = useState(null);
Update the handler to store/toggle an index.
const handleDropdown = (index) =>{
setDropDown(value => value === index ? null : index);
}
In the render simply check if the current index matches the dropdown state.
<div
onMouseEnter={() => handleDropdown(index)}
onMouseLeave={() => handleDropdown(index)} // <-- handle index in mouse leave
className={`chat__message ${
item.name === user.displayName && "chat__reciever"
}`}
>
<h5 className={"chat__messageUser"}>{item.name}</h5>
<p className={"chat__messagetext"}>{item.message}</p>
{dropdown === index && ( // <-- check index match with dropdown value
<Button>
<ArrowDropDownIcon />
</Button>
)}
{item.img && (
<img alt={""} className={"chat__img"} src={item.imgURL} />
)}
</div>