I am working on country selector component I want to update the state value only when user click on submit button.
I tried to store selected drop down value in temp variable But the select drop down value will not reflect in UI again get the old value in UI
HeaderLeftComponent (Here i am using drop down state)
const HeaderLeftSection = ( props ) => {
const [showCountry, setShowCountry] = useState(false);
const [ShowAccount, setShowAccount] = useState(false);
const [ShowWishlist, setShowWishlist] = useState(false);
const [countrySelector, setCountrySelector] = useState({
country: "IN",
currency: "INR"
});
const handleShowCountry = () => {
setShowCountry(true);
}
const handleHideCountry = () => {
setShowCountry(false);
}
const handleShowAccount = () => {
setShowAccount(true);
}
const handleHideAccount = () => {
setShowAccount(false);
}
const handleShowWishlist = () => {
setShowWishlist(true);
}
const handleHideWishlist = () => {
setShowWishlist(false);
}
const handleCountryAndCurrencyChange = (item) => {
const Country = item;
}
const handleSaveCountry = (item) => {
alert();
setShowCountry(false);
}
return (
<>
<Model
show={showCountry}
hide={ handleHideCountry }
classModal="Country__selector--modal"
handleButtonClicked={handleSaveCountry}
ModelHeader="true"
ModelFooter="true"
ButtonOneText="Save & Continue"
ButtonSecondText="Cancel"
block="true">
<CountrySelector
countrySelector={countrySelector}
handleCountryAndCurrencyChange={handleCountryAndCurrencyChange}
/>
</Model>
<Model
show={ShowAccount}
hide={handleHideAccount}
classModal="Account__form--modal"
ModelHeader="true"
ModelHeaderText="SIGN IN">
<Login />
</Model>
<Model show={ShowWishlist} hide={handleHideWishlist} ModelHeader="true" ModelHeaderText="WISHLIST" classModal="Wishlist__form--modal">
<p>WishList Test</p>
</Model>
<div className="Header__left-section">
<div className="Header__left--countrySelector mr-5 icon" onClick={handleShowCountry}>
<span>{countrySelector.country}</span>
<span>{countrySelector.currency}</span>
</div>
<div className="Header__left--account mr-5">
<Icon.Person className="icon" onClick={handleShowAccount}/>
</div>
<div className="Header__left--wishlist">
<Icon.Heart className="icon" onClick={handleShowWishlist}/>
</div>
</div>
</>
)
}
export default HeaderLeftSection;
Country Selector Component
const CountrySelector = ( props ) => {
return (
<>
<p>Shipping somewhere else?</p>
<p>Simply select the destination you would like to deliver to and the currency you would like to shop in.</p>
<Select List={countryList} label="Delivery To" size="sm" value={props.countrySelector.country} handleCountryAndCurrencyChange={props.handleCountryAndCurrencyChange}/>
<Select List={currencyList} label="Shop in" size="sm" value={props.countrySelector.currency} handleCountryAndCurrencyChange={props.handleCountryAndCurrencyChange}/>
</>
)
}
Below is the select drop down component
const Select = ( props ) => {
return (
<>
<Form.Group>
<Form.Label>{props.label}</Form.Label>
<Form.Control as="select" size={props.size} value={props.value} custom onChange={(event) => props.handleCountryAndCurrencyChange(event.target.value)}>
{
props.List.map((item, index) => {
return <option key={item.code} value={item.code}>{item.name}</option>
})
}
</Form.Control>
</Form.Group>
</>
)
}
Expected :
When user selects on country selector drop down It should reflect on UI (User can see) but its state should not update. State only update only when user click on submit button
Thanks in advance
Related
Hello everyone and thank you for reading this! Here is my problem that i can't solve:
My application has the following functionality:
There are 2 inputs, then a button, when clicked, 2 more inputs appear and a button to send data from all inputs to the console, however, in the additional field, one input is required. This is where my problem arises: now, if I called additional inputs and filled in all the data, they are transferred to the console, if I didn’t fill in the required field, an error message goes to the console, BUT. I also need, in the event that I did NOT call additional inputs, the data of 2 basic inputs was transferred to the console. At the moment I can't figure it out.
import React, { useState } from "react";
import ReactDOM from "react-dom/client";
import produce from "immer";
const FunctionalBlock = ({
id,
idx,
isDeleted,
toggleBlockState,
additionalValue,
additionalTitle,
setNewBlock,
index,
}) => {
return (
<div
style={{
display: "flex",
maxWidth: "300px",
justifyContent: "space-between",
}}
>
{!isDeleted ? (
<React.Fragment>
<strong>{idx}</strong>
<input
type="text"
value={additionalTitle}
onChange={(e) => {
const additionalTitle = e.target.value;
setNewBlock((currentForm) =>
produce(currentForm, (v) => {
v[index].additionalTitle = additionalTitle;
})
);
}}
/>
<input
type="text"
value={additionalValue}
onChange={(e) => {
const additionalValue = e.target.value;
setNewBlock((currentForm) =>
produce(currentForm, (v) => {
v[index].additionalValue = additionalValue;
})
);
}}
/>
<button onClick={toggleBlockState}>now delete me</button>
</React.Fragment>
) : (
<button onClick={toggleBlockState}>REVIVE BLOCK</button>
)}
</div>
);
};
const Application = () => {
const [newBlock, setNewBlock] = useState([]);
const [firstInput, setFirstInput] = useState("");
const [secondInput, setSecondInput] = useState("");
const getNewBlock = (idx) => ({
id: Date.now(),
idx,
isDeleted: false,
additionalValue: "",
additionalTitle: "",
});
const toggleIsDeletedById = (id, block) => {
if (id !== block.id) return block;
return {
...block,
isDeleted: !block.isDeleted,
};
};
const createOnClick = () => {
const block = getNewBlock(newBlock.length + 1);
setNewBlock([...newBlock, block]);
};
const toggleBlockStateById = (id) => {
setNewBlock(newBlock.map((block) => toggleIsDeletedById(id, block)));
};
const showInputData = () => {
newBlock.map((item) => {
if (item.additionalTitle.length < 3) {
console.log("it is less than 3");
} else if (!item.additionalTitle && !item.additionalValue) {
console.log(firstInput, secondInput);
} else {
console.log(
firstInput,
secondInput,
item.additionalTitle,
item.additionalValue
);
}
});
};
return (
<div>
<div>
<input
type="text"
value={firstInput}
onChange={(e) => {
setFirstInput(e.target.value);
}}
/>
<input
type="text"
value={secondInput}
onChange={(e) => {
setSecondInput(e.target.value);
}}
/>
</div>
<div>
<button onClick={createOnClick}>ADD NEW INPUTS</button>
</div>
<div>
{newBlock.map((block, index) => (
<FunctionalBlock
key={index}
{...block}
toggleBlockState={() => toggleBlockStateById(block.id)}
setNewBlock={setNewBlock}
index={index}
/>
))}
</div>
<button onClick={showInputData}>send data</button>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Application />);
Here is this code on sandbox for those who decided to help me. Thank you!
https://codesandbox.io/s/vigilant-booth-xnef6t
function Input() {
const [input, setInput] = useState("");
function handleSearch() {
let url = "https://google.com/search?q=${input}"
window.open(url)
}
return (
<div className="input-wrap">
<input
type="text"
className="input__search"
placeholder="Enter your search..."
value={input}
onChange={(e) => setInput(e.target.value)}></input>
<button
className="input__search--btn"
onClick={handleSearch}>
<i className="fa-solid fa-magnifying-glass"></i>
</button>
</div>
);
}
The search button when clicked will redirect you to a google search based on the value from the input field, below is the site for advanced search, when active the link will add an additional link after "https://google.com/search?q=${input}+site%3A${activepage}.com, how do I check if one or many sites are active then pass down its name to url
P/s: code for toggling websites
function WebButton({ icon, name }) {
const [active, setActive] = useState(false);
function handleToggle() {
setActive(!active);
}
return (
<button
className={active ? "websites-btn active" : "websites-btn"}
onClick={handleToggle}>
<i className={icon}></i>
<div className="websites-name">{name}</div>
</button>
);
}
You can keep a root level state to gather active links to a state. And pass it to the Input component.
Update your Input component to accept array called `` and update the handleSearch to use OR operation in google search.
function Input({ activeLinks }) {
const [input, setInput] = useState("");
function handleSearch() {
if (activeLinks.length > 0) {
let compundSearchURL = `https://google.com/search?q=${input}`;
activeLinks.forEach((link, i) => {
compundSearchURL += `+${i > 0 ? "OR+" : ""}site%3A${link}.com`;
});
window.open(compundSearchURL);
} else {
window.open(`https://google.com/search?q=${input}`);
}
}
return (
<div className="input-wrap">
<input
type="text"
className="input__search"
placeholder="Enter your search..."
value={input}
onChange={(e) => setInput(e.target.value)}
></input>
<button className="input__search--btn" onClick={handleSearch}>
<i className="fa-solid fa-magnifying-glass">Search</i>
</button>
</div>
);
}
Accept another function in WebButton called toggleActiveLink and a string called value which refers to the URL part. Call the function with the value inside handleToggle function.
function WebButton({ icon, name, toggleActiveLink, value }) {
const [active, setActive] = useState(false);
function handleToggle() {
setActive(!active);
toggleActiveLink(value);
}
return (
<button
className={active ? "websites-btn active" : "websites-btn"}
style={{ color: active ? "blue" : "unset" }}
onClick={handleToggle}
>
<i className={icon}></i>
<div className="websites-name">{name}</div>
</button>
);
}
In the main component you have to create a local state to handle the active links. Create the toggle function as given. It will add the value if it is not there otherwise remove it.
const urls = [
{ name: "Reddit", value: "reddit" },
{ name: "Quora", value: "quara" },
{ name: "Facebook", value: "facebook" },
{ name: "Stackoverflow", value: "stackoverflow" },
{ name: "Twitter", value: "twitter" }
];
function App() {
const [activeLinks, setActiveLinks] = useState([]);
const toggleActiveLink = (link) => {
const index = activeLinks.indexOf(link);
if (index < 0) {
setActiveLinks((prevLinks) => [...prevLinks, link]);
} else {
setActiveLinks((prevLinks) => prevLinks.filter((l) => l !== link));
}
};
return (
<>
<Input activeLinks={activeLinks} />
<div>
{urls.map(({ name, value }) => (
<WebButton
name={name}
value={value}
toggleActiveLink={toggleActiveLink}
/>
))}
</div>
</>
);
}
i have a backend api build on nodejs. in the code the api return some categories array.
i ran .map() function on the array to display the array in the JSX.
after that i added a checkBox to each object inside the array.
so what im trying to do is if the checkbox is true its will added another h1 Element (JSX).
Only to the object i clicked on the checkbox.
i tryied to add "status" props and make it false or true and then catch it with onClick e.target.status?
"YES" : "NO"
also, i tried to added checkBox useState and make it true or false . and its work. but not as i want
its display Yes or No to the all objects and not only to the on i clicked on.
const Category = ({ history }) => {
const dispatch = useDispatch()
const user = useSelector((state) => state.waiter)
const selectedCategory = useSelector((state) => state.selectedTable)
const [currectCategory, setCurrectCategory] = useState([])
const [categoryName, setCategoryName] = useState("")
const [categoryIMG, setCategoryIMG] = useState("not found")
const [checkBox, setCheckBox] = useState("false")
useEffect(() => {
if (!user.name) {
history.push('/login')
} else {
(async () => {
const res = await fetch('http://localhost:1000/categories/' + selectedCategory)
const data = await res.json()
setCurrectCategory(data.CurrectCountry.subcategories.map(sc => sc.name))
setCategoryName(data.CurrectCountry.name)
setCategoryIMG(data.CurrectCountry.img)
})()
}
}, [user])
const goBack = () => {
dispatch({
type: 'ALL_CATEGORIES'
})
history.push('/login')
}
const handleCheck = (e) => {
setCheckBox(e.target.checked.toString())
console.log(e.target.checked)
}
return (
<>
<Button className="LogButton" color="secondary" onClick={goBack}>back</Button>
<div className="SingleCategory">
<h1>{categoryName}</h1>
<ListGroup>
{currectCategory.map(category => {
return (
<Row className="Col-padd" key={category}>
<div>
<InputGroup className="mb-3">
<b className="ItemName"> {category} </b>
<img src={categoryIMG} height="100" width="100" ></img>
<FormCheck id={category} className="Checkbox" onChange={handleCheck}></FormCheck>
{checkBox == "true" ? <b>yes</b> : <b>No</b>}
</InputGroup>
</div>
</Row>
)
})}
</ListGroup>
</div>
</>
)
}
Thanks for help !!
You are only creating a single value for the checkbox. If you want to show for all the checkbox, if you have to track the value for each checkbox shown below,
const [checkBox, setCheckBox] = useState({}); // checkBoxName: value
const handleCheck = (e) => {
setCheckBox((prev) => {...prev, [e.target.name]: e.target.value};
}
{!!checkBox['name'] === true ? <b>yes</b> : <b>No</b>}
//change the attribute according to your implementation.
Your problem is that you're just creating a single value for the checkbox and not separating the individual checkboxes. You could solve this in many different ways, but you would be well served by extracting the code for your checkbox to a separate component.
const Checkbox = ({ category, categoryIMG }) => {
const [isChecked, setIsChecked] = useState(false);
const handleCheck = () => {
setIsChecked((prevState) => !prevState);
};
return (
<Row className="Col-padd" key={category}>
<div>
<InputGroup className="mb-3">
<b className="ItemName"> {category} </b>
<img src={categoryIMG} height="100" width="100"></img>
<FormCheck id={category} className="Checkbox" onChange={handleCheck}></FormCheck>
{isChecked == 'true' ? <b>yes</b> : <b>No</b>}
</InputGroup>
</div>
</Row>
);
};
With a separate checkbox component like above you could instantiate it like this in the map:
<ListGroup>
{currectCategory.map((category) => (
<Checkbox category={category} categoryIMG={categoryIMG} />
))}
</ListGroup>
I have a parent and a child component, child component has a button, which I'd like to disable it after the first click. This answer works for me in child component. However the function executed on click now exists in parent component, how could I pass the attribute down to the child component? I tried the following and it didn't work.
Parent:
const Home = () => {
let btnRef = useRef();
const handleBtnClick = () => {
if (btnRef.current) {
btnRef.current.setAttribute("disabled", "disabled");
}
}
return (
<>
<Card btnRef={btnRef} handleBtnClick={handleBtnClick} />
</>
)
}
Child:
const Card = ({btnRef, handleBtnClick}) => {
return (
<div>
<button ref={btnRef} onClick={handleBtnClick}>Click me</button>
</div>
)
}
In general, refs should be used only as a last resort in React. React is declarative by nature, so instead of the parent "making" the child disabled (which is what you are doing with the ref) it should just "say" that the child should be disabled (example below):
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({isDisabled, onButtonClick}) => {
return (
<div>
<button disabled={isDisabled} onClick={onButtonClick}>Click me</button>
</div>
)
}
Actually it works if you fix the typo in prop of Card component. Just rename hadnlBtnClick to handleBtnClick
You don't need to mention each prop/attribute by name as you can use javascript Object Destructuring here.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = (props) => {
return (
<div>
<button {...props}>Click me</button>
</div>
)
}
You can also select a few props and use them differently in the child components. for example, see the text prop below.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card text="I'm a Card" isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({text, ...restProps}) => {
return (
<div>
<button {...restProps}>{text}</button>
</div>
)
}
I have a state
const [ideas, setIdeas] = useState([{title:"test", favourite:false]);
Component Idea.jsx returns props.title and a button "fav".
App.jsx maps through the idea[] and renders each idea.title in
<Item title = {idea.title}/>
on the page.
Problem:
Every time when "fav" is clicked I want to toggle ideas[index].favourite.
How to change a value of favourite only for an idea that was clicked?
How to add this exact idea to the array favourites[]?
App.jsx
function App() {
const [ideas, setIdeas] = useState([{title:"test",
favourite:false}]);
const [isClicked, setIsClicked] = useState(false)
function showAllIdeas () {
setIsClicked(prevValue => {
return !prevValue
}
)
}
function mapIdeas(){return ideas.map((ideaItem, index) => {
return (<Idea
key = {index}
id = {index}
title = {ideaItem.title}
/>
);
})}
return ( <div>
<Fab color="primary" onClick={showAllIdeas}>{expandText()}</Fab>
{isClicked && mapIdeas()}
</div>)
}
Item.jsx
function Idea(props) {
const [isClicked, setIsClicked] = useState(false)
function handleClick(){
setIsClicked(prevValue => {
return !prevValue
})
}
console.log(isClicked)
return(
<div className={"idea-list" } ><p>{props.title} {isClicked ?
<StarIcon onClick={handleClick}/> :<StarBorderIcon onClick=.
{handleClick}/>}</p>
</div>
)
}
const handleFavToggle = (index) => {
setItems(items=> {
const data = [...items]
data[index] = {...data[index],favourite: !data[index].favourite }
return data
})
}
<Item key={index} title={item.title} index={index} handleFavToggle={handleFavToggle}/>
In item component you have to handle click with handleFavToggle and pass all params