title: Common practice for setting state for dynamically-mapped dropdown values in React using Material-ui?
body: <p>On a page, I have an account that queries a firebase database for documents under the user's purchases, and it gets the data and passes it into a state object. All of the data is mapping perfectly, but I can't get dropdowns for the final loop.</p>
  <p><a href="https://i.stack.imgur.com/5VELt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/5VELt.png" alt="The product" /></a></p>
  <p>This is the representation of each product in their purchases. Each product has a 'pack' that has various resolutions available. When the user clicks the 'download' dropdown, I need the user to be able to select from [4k, 1080w, 1080sq, 1080v, all] to add to the bucket file path for the s3 presignedURL.</p>
  <p>There could be 1 pack, and there could be 20. There could be 2 options, and there could be 10. Therefore, mapping is required. However, I can't seem to dynamically access the target of the object in the Menu's anchorEl while using a ClickAwayListener, because the click technically happens over the set anchorEl. I feel like I can use one state object, and I'm missing something to make it work.</p>
  <p>My render method:</p>
  <pre><code>import { useAuth } from '../contexts/AuthContext'
  import { Accordion, AccordionActions, AccordionSummary, AccordionDetails, Button, CircularProgress, ClickAwayListener, Grid, Menu, MenuItem, Typography } from '@material-ui/core'
  import { ExpandLess, ExpandMore as ExpandMoreIcon } from '@material-ui/icons'
  import LoginForm from '../components/LoginForm'
  import { motion } from 'framer-motion'
  import { useRouter } from 'next/router'
  import { useEffect, useState } from 'react'
  import { db, functions } from '../firebase'
  import styles from '../styles/Account.module.scss'
  
  export default function Account() {
    const { currentUser, getIdToken } = useAuth()
    const [userPurchases, setUserPurchases] = useState([])
    const [anchorEl, setAnchorEl] = useState(null)
    const [generatingURL, setGeneratingURL] = useState(false)
  
    function openDownloads(e) {
      setAnchorEl(prevState =&gt; (e.currentTarget))
    }
  
    function handleClose() {
      setAnchorEl(null)
    }
  
    function generateLink(prefix, variationChoice, pack) {
      setGeneratingURL(true)
      const variation = variationChoice ? `${variationChoice}/` : ''
      console.log('link: ', `edit-elements/${prefix}${variation}${pack}.zip`)
      setGeneratingURL(false)
      return
      if (pack.downloads_remaining === 0) {
        console.error(&quot;No more Downloads remaining&quot;)
        setGeneratingURL(false)
        handleClose()
        return
      }
      handleClose()
      const genLink = functions.httpsCallable('generatePresignedURL')
      genLink({
        fileName: pack,
        variation: variation,
        prefix: prefix
      })
      .then(res =&gt; {
        console.log(JSON.stringify(res))
        setGeneratingURL(false)
      })
      .catch(err =&gt; {
        console.log(JSON.stringify(err))
        setGeneratingURL(false)
      })
    }
  
    useEffect(() =&gt; {
      if (currentUser !== null) {
        const fetchData = async () =&gt; {
        // Grab user products_owned from customers collection for user UID
        const results = await db.collection('customers').doc(currentUser.uid).get()
        .then((response) =&gt; {
          return response.data().products_owned
        })
        .catch(err =&gt; console.log(err))
  
          Object.entries(results).map(([product, fields]) =&gt; {
            // Grabbing each product document to get meta (title, prefix, image location, etc [so it's always current])
            const productDoc = db.collection('products').doc(product).get()
            .then(doc =&gt; {
              const data = doc.data()
              const productMeta = {
                uid: product,
                title: data.title,
                main_image: data.main_image,
                product_prefix: data.product_prefix,
                variations: data.variations
              }
              // This is where we merge the meta with the customer purchase data for each product
              setUserPurchases({
                ...userPurchases,
                [product]: {
                  ...fields,
                  ...productMeta
                }
              })
            })
            .catch(err =&gt; {
              console.error('Error retrieving purchases. Please refresh page to try again. Full error: ', JSON.stringify(err))  
            })
          })
        }
      return fetchData()
      }
    }, [currentUser])
  
    if (userPurchases.length === 0) {
      return (
        &lt;CircularProgress /&gt;
      )
    }
  
    return(
      currentUser !== null &amp;&amp; userPurchases !== null ? 
        &lt;&gt;
          &lt;p&gt;Welcome, { currentUser.displayName || currentUser.email }!&lt;/p&gt;
          &lt;Typography variant=&quot;h3&quot; style={{marginBottom: '1em'}}&gt;Purchased Products:&lt;/Typography&gt;
          { userPurchases &amp;&amp; Object.values(userPurchases).map((product, id) =&gt; {
            const purchase_date = new Date(Date(product.purchase_date * 1000)).toLocaleDateString()
            return (
              &lt;motion.div key={id}&gt;
                &lt;Accordion style={{backgroundColor: '#efefef'}}&gt;
                  &lt;AccordionSummary expandIcon={&lt;ExpandMoreIcon style={{fontSize: &quot;calc(2vw + 10px)&quot;}}/&gt;} aria-controls={`${product.title} accordion panel`}&gt;
                    &lt;Grid container direction=&quot;row&quot; alignItems=&quot;center&quot;&gt;
                      &lt;Grid item xs={3}&gt;&lt;img src={product.main_image} style={{ height: '100%', maxHeight: &quot;200px&quot;, width: '100%', maxWidth: '150px' }}/&gt;&lt;/Grid&gt;
                      &lt;Grid item xs={6}&gt;&lt;Typography variant=&quot;h6&quot;&gt;{product.title}&lt;/Typography&gt;&lt;/Grid&gt;
                      &lt;Grid item xs={3}&gt;&lt;Typography variant=&quot;body2&quot;&gt;&lt;b&gt;Purchase Date:&lt;/b&gt;&lt;br /&gt;{purchase_date}&lt;/Typography&gt;&lt;/Grid&gt;
                    &lt;/Grid&gt;
                  &lt;/AccordionSummary&gt;
                  &lt;AccordionDetails style={{backgroundColor: &quot;#e5e5e5&quot;, borderTop: 'solid 6px #5e5e5e', padding: '0px'}}&gt;
                    &lt;Grid container direction=&quot;column&quot; className={styles[`product-grid`]}&gt;
                      {Object.entries(product.packs).map(([pack, downloads]) =&gt; {
                        return (
                          &lt;Grid key={ `${pack}-container` } container direction=&quot;row&quot; alignItems=&quot;center&quot; justify=&quot;space-between&quot; style={{padding: '2em 1em'}}&gt;
                            &lt;Grid item xs={3} s={4} style={{ textTransform: 'uppercase', backgroundColor: 'transparent' }}&gt;&lt;Typography align=&quot;left&quot; variant=&quot;subtitle2&quot; style={{fontSize: 'calc(.5vw + 10px)'}}&gt;{pack}&lt;/Typography&gt;&lt;/Grid&gt;
                            &lt;Grid item xs={4} s={4} style={{ backgroundColor: 'transparent' }}&gt;&lt;Typography align=&quot;center&quot; variant=&quot;subtitle2&quot; style={{fontSize: &quot;calc(.2em + 10px)&quot;}}&gt;{`DL left: ${downloads.downloads_remaining}`}&lt;/Typography&gt;&lt;/Grid&gt;
                            &lt;Grid item xs={5} s={4} style={{ backgroundColor: 'transparent' }}&gt;
                              &lt;Button id={`${pack}-btn`} disabled={generatingURL} fullWidth onClick={openDownloads} color='primary' endIcon={&lt;ExpandMoreIcon styles={Boolean(anchorEl) ? { transform: &quot;rotate(180deg)&quot; } : { transform: &quot;rotate(0deg)&quot; } }/&gt;}&gt;
                                &lt;Typography variant=&quot;button&quot;&gt;{!generatingURL ? 'Downloads' : 'Processing'}&lt;/Typography&gt;
                              &lt;/Button&gt;
                              &lt;ClickAwayListener mouseEvent={ 'onMouseDown' } onClickAway={handleClose}&gt;
                                &lt;Menu anchorOrigin={{ vertical: 'top', horizontal: 'right' }} transformOrigin={{vertical: 'top', horizontal: 'right'}}id={`${product}-variations`} open={Boolean(anchorEl)} anchorEl={anchorEl}&gt;
                                  {product.variations &amp;&amp; &lt;MenuItem onClick={() =&gt; generateLink(product.product_prefix, null, pack)}&gt;All&lt;/MenuItem&gt;}
                                  {product.variations ? Object.entries(product.variations).map(([name, variation]) =&gt; {
                                    return(
                                      &lt;MenuItem disabled={ downloads === 0 } onClick={ () =&gt; generateLink(product.product_prefix, variation, pack) } key={name}&gt;{name}&lt;/MenuItem&gt;
                                    )}) : 
                                    &lt;MenuItem onClick={() =&gt; generateLink(product.product_prefix, null, pack )}&gt;Download&lt;/MenuItem&gt;
                                  }
                                &lt;/Menu&gt;
                              &lt;/ClickAwayListener&gt;
                            &lt;/Grid&gt;
                          &lt;/Grid&gt;
                        )}
                      )}
                    &lt;/Grid&gt;
                  &lt;/AccordionDetails&gt;
                &lt;/Accordion&gt;
              &lt;/motion.div&gt;
            )
          }) 
        }
      &lt;/&gt;
      :
      &lt;&gt;
        &lt;p&gt;No user Signed in&lt;/p&gt;
        &lt;LoginForm /&gt;
      &lt;/&gt;
    )
  }
  </code></pre>
  <p>I just need to be able to identify which dropdown is being clicked to set the anchorEl, and then to be able to click away to close it. The issue is I'm not sure how to handle the state. My quest to find the best practice of a situation like this has been fruitless. Maybe there's a resource that I'm missing? Thanks in advance!</p>