I would like to know how can I make any button changing color when selected.
Knowing that you can only selected one button at the time for each value.
Like if you click on 'White', the Black button can't have the same background color. Same for the Size, if 'L' is selected, and you click on 'S' then, 'L' take the default color and 'S' the background color when an element is selected.
Thanks in advance to anyone who tries to help me!
I've tried to use If Statement but I'm not sure I'm doing it right.
{values.map((value) => {
const id = `option-${name}-${value}`;
const checked = setSelectedOption[name] === value;
const [bgSelected, setBgSelected] = useState(false);
const handleClick = () => {
setBgSelected(!bgSelected);
}
const handleSelected = () => {
setSelectedOption(name, value);
if (bgSelected === true) {
setBgSelected(false);
handleClick()
} else {
setBgSelected(true);
handleClick()
}
}
return (
<div key={id} className="product-option-value text-left">
<div
type="button"
checked={checked}
name={name}
value={value}
id={id}
// onChange={() =>setSelectedOption(name, value)}
className=""
/>
<label
htmlFor={id}
//onClick={handleClick}
className={bgSelected ? "bg-blue-800" : "bg-blue-200"}
onClick={handleSelected}
>{value}</label>
</div>
)
})}
</div>
CodePudding user response:
It seems like you are calling the wrong property from useProductOptions
to determine if your product has been selected, you are using setSelectedOption[name]
instead of the selectedOptions[name]
.
Based on this one value (selectedOptions[name]
) you can also determine if a value has been selected so you do not need to create an extra state for the background.
Try and use:
function ProductForm({ product }) {
const { options, selectedVariant, selectedOptions, setSelectedOption } =
useProductOptions();
const isOutOfStock = !selectedVariant?.availableForSale || false;
return (
<div className="drop-shadow-xl bg-white rounded-xl p-10 w-7/12">
<h1 className="font-bold text-3xl">{product.title}</h1>
<ProductPrice
className="font-light text-lg pb-3"
data={product}
variantId={selectedVariant.id}
/>
<div className="product-option">
{options.map(({ name, values }) => {
if (values.length === 1) {
return null;
}
return (
<div
key={name}
className="product-option-group sm:grid-cols-2 flex items-center gap-3"
>
<legend className="product-option-name font-bold text-lg py-3 w-12">
{name}
</legend>
{values.map((value) => {
const id = `option-${name}-${value}`;
const checked = selectedOptions[name] === value;
return (
<div key={id} className="product-option-value text-left">
<div
type="button"
checked={checked}
name={name}
value={value}
id={id}
className=""
/>
<label
htmlFor={id}
className={checked ? "bg-blue-800" : "bg-blue-200"}
onClick={() => setSelectedOption(name, value)}
>
{value}
</label>
</div>
);
})}
</div>
);
})}
</div>
<AddToCartButton
disabled={isOutOfStock}
className="add-to-cart font-bold text-md p-2 rounded-3xl mt-3 bg-slate-300 hover:bg-slate-400"
>
{isOutOfStock ? "Out of stock" : "Add to cart"}
</AddToCartButton>
</div>
);
}
=== old answer before the post update ===
I am not sure why your code is not throwing errors, but you should
(and can) not use a useState
inside of a map
.
You could write this component using just one state, as below.
A working example can be found at this codesandbox
const values = [
{
id: 1,
name: "test",
value: "option 1"
},
{
id: 2,
name: "test",
value: "option 2"
},
{
id: 3,
name: "test",
value: "option 3"
}
];
const MyComponent = () => {
const [selectedValue, setSelectedValue] = useState(values[0]);
return (
<>
{values.map(({ id, value, name }) => {
const isSelected = selectedValue === id;
return (
<div key={id} className="product-option-value text-left">
<input
type="radio"
checked={isSelected}
name={name}
value={value}
id={id}
onChange={() => setSelectedValue(id)}
/>
<label
htmlFor={id}
style={{ background: isSelected ? "red" : "blue" }}
>
{value}
</label>
</div>
);
})}
</>
);
};