'React Material UI Select conditional rendering shows value as out of range

The use case is creating an order that has multiple order lines. I have two lists of Products: Preferred and All. Every item in Preferred is in All. I have a boolean state variable ShowPreferredProducts to toggle between which list is passed in the Select to render the MenuItem list.

<Select
  label="itemNumber"
  {(showPreferredProducts ? preferredProducts : allProducts) 
    .map((product, idx) => (
      <Mui.MenuItem
        key={`${product.itemNumber}_${idx}`}
        value={product.itemNumber}
        />
          {product.itemName}
      </Mui.MenuItem>
      ))}
      <ListSubheader sx={{ position: 'sticky', bottom: '0' }}>
        <Mui.Box sx={{ pointerEvents: 'none' }}>
          <Mui.Button
            sx={{ pointerEvents: 'all' }}
            onClick={(event) => {
              setShowPreferredProducts((prev) => !prev);
              }}>
                {showPreferredProducts ? 'Show Preferred Products' : 'Show All Products'}
           </Mui.Button>
         </Mui.Box>
       </ListSubheader>
     </Select>

When the button is clicked, we re-render the MenuItem list in the Select. When a user selects a product that is in All, but not in Preferred, while showPreferredProducts is true, any products in list All now display as empty (as it cannot find the value in the currently rendered list).

The user wants to always see the Preferred list first (at least for new order lines). A hacky way to keep all items being displayed is to force showPreferredProducts to false in an onClose and it to true in an onOpen. This does not fix the display issue while order lines are being edited, but it does keep the display looking as it should when the order is not being updated.

What is the preferred way of solving this? It "works", but it feels like there is a better way.

Edit:

Found a working solution. There may be a better way but this works and does not cause warnings and correctly allows for searching as well.

Since preferredProducts is a subset of allProducts, we render allProducts and at the MenuItem level we choose to display or not based on some functional programming.

// ensure product's item number and the MenuItem's 
// item number is equal since this is done on the `MenuItem` level.
const isProductInSearchValue = (product: Product, itemNumber: string) =>
product.itemNumber === itemNumber &&
(!productSearchValue ?? product.SomeValueToSearch.includes(productSearchValue);

// pick a list to filter from based on showPreferredProducts
const isItemVisibleInProductList = (itemNumber: string) =>
  (showPreferredProducts && preferredProducts.some((prod) => isProductInSearchValue(prod, itemNumber))) || 
  (!showPreferredProducts && allProducts.some((prod) => isProductInSearchValue(prod, itemNumber)));


<Select
  label="itemNumber"
  {(allProducts) // render all products not dependent on boolean
    .map((product, idx) => (
      <Mui.MenuItem
        key={`${product.itemNumber}_${idx}`}
        value={product.itemNumber}
        sx={{display: isItemVisibleInProductList(product.itemNumber) ? 'block' : 'none'}} // set display if true
        />
          {product.itemName}
      </Mui.MenuItem>
   ))}
   <ListSubheader sx={{ position: 'sticky', bottom: '0' }}>
     <Mui.Box sx={{ pointerEvents: 'none' }}>
       <Mui.Button
         onClick={(event) => {
           setShowPreferredProducts((prev) => !prev);
        }}>
          {showPreferredProducts ? 'Show Preferred Products' : 'Show All Products'}
       </Mui.Button>
     </Mui.Box>
   </ListSubheader>
</Select>


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source