javascriptreactjsif-statementhydrogenshopify-hydrogen

Can't make the background changing if selected (really hard to resolve) React and Hydrogen


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.

What it look like now

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>

Solution

  • 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>
            );
          })}
        </>
      );
    };