Search code examples
javascripthtmlreactjsmongodbmern

How to dynamically create an array of buttons in React using MonogoDB?


Im trying to convert my static product configurator into a dynamic one using the MERN stack, and I am really close to being done with the test version finally! I have everything working, but I am having trouble making it even more dynamic. In my configurator here for example, I have the ability to change colors through a list of hard coded buttons that trigger an event. This gets tedious with the amount of products that I will be trying to deploy to our website, and some products are only available in one, two, or three materials. I have successfully uploaded an array to MongoDB and is structured like so: enter image description hereenter image description hereenter image description here

So my main question is, how would I go about generating a list of buttons that are nested inside of an accordion like in my example above, while each "section" of the accordion would have the correlating name of that material selection.

Here is my hardcoded snipit of the HTML as well as some of the functions I have currently:

import {useEffect, useState} from "react";
import {useSelector, useDispatch} from "react-redux";

// Actions
import {getProductDetails} from "../redux/actions/productActions";

const ProductScreen = ({match}) => {
  const dispatch = useDispatch();

  const productDetails = useSelector((state) => state.getProductDetails);
  const {loading, error, product} = productDetails;

  useEffect(() => {
    if (product && match.params.id !== product._id) {
      dispatch(getProductDetails(match.params.id));
    }
    setCurrentSrc(product.src)
  }, [dispatch, match, product]);

  const changeColor = (event) => {
    const modelViewer = document.querySelector("model-viewer");
    modelViewer.variantName = event.target.value;
  };

  const [currentSrc, setCurrentSrc] = useState(product.src);

  return (*/HTML/*)
}
<div className="accordion">
  <div>
    <input type="radio" name="accordion" id="Lacquer" className="accordion__input" />
    <label htmlFor="Lacquer" className="materialLabel">Lacquer</label>
    <div className="items">
      <button onClick={changeColor} className='item' value='Ballet Pink'>Ballet Pink</button>
      <button onClick={changeColor} className='item' value="Bellini Peach">Bellini Peach</button>
      <button onClick={changeColor} className='item' value="Boeing Navy">Boeing Navy</button>
      <button onClick={changeColor} className='item' value="Cessna Grey">Cessna Grey</button>
      <button onClick={changeColor} className='item' value="Charter Coral">Charter Coral</button>
      <button onClick={changeColor} className='item' value="Cirrus White">Cirrus White</button>
      <button onClick={changeColor} className='item' value="Citation Blue">Citation Blue</button>
      <button onClick={changeColor} className='item' value="Dash Pink">Dash Pink</button>
      <button onClick={changeColor} className='item' value="Falcon Grey">Falcon Grey</button>
      <button onClick={changeColor} className='item' value="Gulfstream Blue">Gulfstream Blue</button>
      <button onClick={changeColor} className='item' value="Havilland Sage">Havilland Sage</button>
      <button onClick={changeColor} className='item' value="Illuminating Yellow">Illuminating Yellow</button>
      <button onClick={changeColor} className='item' value="Lear Green">Lear Green</button>
      <button onClick={changeColor} className='item' value="Merlin Lavender">Merlin Lavender</button>
      <button onClick={changeColor} className='item' value="Midnight Green">Midnight Green</button>
      <button onClick={changeColor} className='item' value="Polar Ice">Polar Ice</button>
      <button onClick={changeColor} className='item' value="Smoky Blue">Smoky Blue</button>
      <button onClick={changeColor} className='item' value="Stratos Black">Stratos Black</button>
      <button onClick={changeColor} className='item' value="Vitamin C">Vitamin C</button>
    </div>
  </div>
  <div>
    <input type="radio" name="accordion" id="Wood" className="accordion__input" />
    <label htmlFor="Wood" className="materialLabel">Wood</label>
    <div className="items">
      <button onClick={changeColor} className='item' value="Golden Pecan Mappa Burl">Golden Pecan Mappa Burl</button>
      <button onClick={changeColor} className='item' value="Golden Pecan Olive Ash">Golden Pecan Olive Ash</button>
      <button onClick={changeColor} className='item' value="Natural Mappa Burl">Natural Mappa Burl</button>
      <button onClick={changeColor} className='item' value="Natural Olive Ash">Natural Olive Ash</button>
      <button onClick={changeColor} className='item' value="Provincial Mappa Burl">Provincial Mappa Burl</button>
      <button onClick={changeColor} className='item' value="Provincial Olive Ash">Provincial Olive Ash</button>
    </div>
  </div>
</div>

Right now I can console.log(product.material) and see the product JSON, but I am unsure how to extract that information and plug it into a

Here is the redux/axios portion I use to getProductDetails:

export const getProductDetails = (id) => async(dispatch) => {
  try {
    dispatch({type: actionTypes.GET_PRODUCT_DETAILS_REQUEST});
    
    const {data} = await axios.get(`/api/products/${id}`);
    
    dispatch({
      type: actionTypes.GET_PRODUCT_DETAILS_SUCCESS,
      payload: data,
    });
  } catch (error) {
    dispatch({type: actionTypes.GET_PRODUCT_DETAILS_FAIL,
      payload: error.response && error.response.data.message ?
        error.response.data.message :
        error.message,
    });
  }
};

Any help would be greatly appreciated!


Solution

  • You should create a custom component for simplicity representing your input radio and loop

    1. The list of materials object
    2. The list of values inside it

    In ProductScreen:

    {
      product && product.materials[0].map((material) => (
        <CustomRadioButton material={material} changeColor={changeColor} />
      ));
    }
    

    Create a new CustomRadioButton component

    export const CustomRadioButton = ({ material, changeColor }) => {
      const materialName = Object.keys(material);
      if (!materialName) return <></>;
      return (
        <div>
          <input
            type='radio'
            name='accordion'
            id={materialName}
            className='accordion__input'
          />
          <label htmlFor={materialName} className='materialLabel'>
            {materialName}
          </label>
          <div className='items'>
            {material[materialName].map((item) => (
              <button onClick={changeColor} className='item' value={item}>
                {item}
              </button>
            ))}
          </div>
        </div>
      );
    };