Search code examples
reactjsvariantvariable-product

How to create a product page with variant dropdowns in react?


What's the best way to create a product page with variant dropdowns? I have a product with list of variants eg:

[{size: "small", color: "red", material: "metal"},
{size: "small", color: "red", material: "wood"},
{size: "medium", color: "blue", material: "plastic"},
{size: "large", color: "blue", material: "metal"},
{size: "large", color: "yellow", material: "wood"}]

What i did was create 3 dropdowns one for size, one for color and one for material that has all available options listed.

I need a way to affect the other 2 dropdowns when one of the dropdowns is changed so that only the available variations are available in the dropdowns.

For example if the user selects "small" from the first dropdown, the color dropdown should only show red and the material dropdown should only show metal and wood.

What's the best way to achieve this?

I have created the example of 3 dropdowns in this codesandbox: https://codesandbox.io/s/divine-water-vz8tv?file=/src/App.js


Solution

  • You can collect options every time the value of one of the select changes. It remains only to finalize the reset of dependent selects when re-selecting the main (on setSize reset color and material, on setColor reset material).

    Codesandbox

    const products = [
      { size: "small", color: "red", material: "metal" },
      { size: "small", color: "red", material: "wood" },
      { size: "medium", color: "blue", material: "plastic" },
      { size: "large", color: "blue", material: "metal" },
      { size: "large", color: "yellow", material: "wood" }
    ];
    
    export default function App() {
      const [size, setSize] = React.useState();
      const [color, setColor] = React.useState();
      const [material, setMaterial] = React.useState();
    
      const sizeOptions = products
        .map((p) => p.size)
        .filter((v, i, a) => a.indexOf(v) === i)
        .map((size) => ({ label: size, value: size }));
      const colorOptions = products
        .filter((p) => size && p.size === size.value)
        .map((p) => p.color)
        .filter((v, i, a) => a.indexOf(v) === i)
        .map((color) => ({ label: color, value: color }));
      const materialOptions = products
        .filter(
          (p) => size && p.size === size.value && color && p.color === color.value
        )
        .map((p) => p.material)
        .filter((v, i, a) => a.indexOf(v) === i)
        .map((material) => ({ label: material, value: material }));
    
      return (
        <div className="App">
          <Select value={size} onChange={setSize} options={sizeOptions} />
          <Select
            value={color}
            onChange={setColor}
            options={colorOptions}
            isDisabled={!size}
          />
          <Select
            value={material}
            onChange={setMaterial}
            options={materialOptions}
            isDisabled={!color}
          />
        </div>
      );
    }