Search code examples
reactjsmaterial-uichips

Chip component of MUI in React not rendering on first click


I am using MUI Select for multi-select option and showing the final values after some modification as MUI Chips. Now when i click add the chip does not shows at that instant however If I change any value of name and suffix the page renders and then the chip shows. Maybe i am missing something. Any help is appreciated.

import { useState,  } from 'react';
import styled from 'styled-components';
import {OutlinedInput, MenuItem, Chip, useTheme, Select} from '@mui/material';

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
      width: 250,
    },
  },
};

const EditUser =styled.div`
  display: flex;
  align-items: center;
  justify-content: flex-start;
  margin-left:5px;
  margin-top: 10px;
  margin-bottom: 20px;
`
const Label = styled.label`
    width: 100px;
    margin: 10px;
`
const LabelInputWrapper = styled.div`
    margin-bottom:25px;
    flex:1 0 30%;
    display: ${(props) => props.disp};

`
const Adduserformcont = styled.div`
  display: flex;
  flex-wrap:wrap;
  margin:10px;
`
const AddChrgWrapper = styled.div`
    display: flex;
`

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

function getStyles(name, personName, theme) {
  return {
    fontWeight:
      personName.indexOf(name) === -1
        ? theme.typography.fontWeightRegular
        : theme.typography.fontWeightMedium,
  };
}
const desigSuffix = ["I","II", "III", "IV"];

const EditUsers = () => {
  const theme = useTheme();
  const [personName, setPersonName] = useState([]);
  const [value, setValue] = useState({})
  const [finalValue, setFinalValue] =useState([])

  
  const onChangeUserForm = (event) => {
    if(event.target.name === 'selectMultiAddnCharg'){
      setPersonName(event.target.value)
    }
    let temp = {...value, [event.target.name]:event.target.value}
    setValue(temp);
    console.log(temp)
  
  }
  const addWithSuffix = () => {
    let tempArray = finalValue;
    tempArray.push(`${value.adnCharge} (${value.desgnSuffix})`)
    setFinalValue(tempArray)
    
  } 

  return (

    <>

    <form> 

        <AddChrgWrapper>
        <Label>Select name</Label>
        <Select
          name='selectMultiAddnCharg'
          multiple
          displayEmpty
          value={personName}
          onChange={onChangeUserForm}
          input={<OutlinedInput />}
          renderValue={(selected) => {
            if (selected.length === 0) {
              return <em>Select name</em>;
            }

            return selected.join(', ');
          }}
          MenuProps={MenuProps}
          inputProps={{ 'aria-label': 'Without label' }}
        >
          <MenuItem disabled value="">
            <em>Placeholder</em>
          </MenuItem>
          {names.map((name) => (
            <MenuItem
              key={name}
              value={name}
              style={getStyles(name, personName, theme)}
            >
              {name}
            </MenuItem>
          ))}
        </Select>
        </AddChrgWrapper>
      <LabelInputWrapper name="adnCharge">
                <Label>Name</Label>
                <select name="adnCharge" onChange={onChangeUserForm}>                
                    <option>Select</option>
                    {personName.map((item,index) => <option key={index}>{item}</option>)}
                </select>  
      </LabelInputWrapper>
      <LabelInputWrapper name="desgnSuffix" >
                <Label>Suffix</Label>
                <select name="desgnSuffix" onChange={onChangeUserForm}>                
                    <option>Select</option>
                    {desigSuffix.map((item,index) => <option key={index}>{item}</option>)}
                </select>  
      </LabelInputWrapper> 
      <button type="button" onClick={(e)=>addWithSuffix(e)}>Add</button>
      <div>{finalValue.map((item, index)=><Chip key={index} label={item}/>)}</div>   
      </form>
    </>
  )
}

export default EditUsers


Solution

  • In addWithSuffix function, When you declared tempArray it's not a new array, but you still mutate the finalValue state directly, and this's not allowd. From React docs:

    state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state.

    You can declare new array variable based on old state
    let tempArray = [...finalValue]
    or use first argument of setFinalValue that is old state

    const addWithSuffix = () => {
        let tempArray = [...finalValue];
        tempArray.push(`${value.adnCharge} (${value.desgnSuffix})`)
        setFinalValue(tempArray)
    }
    

    OR

    const addWithSuffix = () => {
       setFinalValue(state => state.concat(`${value.adnCharge} (${value.desgnSuffix})`))
    }
    

    Demo:

    const names = [
      'Oliver Hansen',
      'Van Henry',
      'April Tucker',
      'Ralph Hubbard',
      'Omar Alexander',
      'Carlos Abbott',
      'Miriam Wagner',
      'Bradley Wilkerson',
      'Virginia Andrews',
      'Kelly Snyder',
    ];
    
    function getStyles(name, personName, theme) {
      return {
        fontWeight:
          personName.indexOf(name) === -1
            ? theme.typography.fontWeightRegular
            : theme.typography.fontWeightMedium,
      };
    }
    const desigSuffix = ["I","II", "III", "IV"];
    
    const EditUsers = () => {
    const { useState } = React
    const { OutlinedInput, MenuItem, Chip, useTheme, Select } = MaterialUI;
      const theme = useTheme();
      const [personName, setPersonName] = useState([]);
      const [value, setValue] = useState({})
      const [finalValue, setFinalValue] =useState([])
    
      
      const onChangeUserForm = (event) => {
        if(event.target.name === 'selectMultiAddnCharg'){
          setPersonName(event.target.value)
        }
        let temp = {...value, [event.target.name]:event.target.value}
        setValue(temp);
        console.log(temp)
      
      }
      const addWithSuffix = () => {
        // let tempArray = finalValue;
        const newVal = `${value.adnCharge} (${value.desgnSuffix})`
        // tempArray.push(newVal)
        setFinalValue(state => state.concat(newVal))
      } 
    
      return (
    
        <>
    
        <form> 
    
            <div>
            <label>Select name</label>
            <Select
              name='selectMultiAddnCharg'
              multiple
              displayEmpty
              value={personName}
              onChange={onChangeUserForm}
              input={<OutlinedInput />}
              renderValue={(selected) => {
                if (selected.length === 0) {
                  return <em>Select name</em>;
                }
    
                return selected.join(', ');
              }}
    
              inputProps={{ 'aria-label': 'Without label' }}
            >
              <MenuItem disabled value="">
                <em>Placeholder</em>
              </MenuItem>
              {names.map((name) => (
                <MenuItem
                  key={name}
                  value={name}
                  style={getStyles(name, personName, theme)}
                >
                  {name}
                </MenuItem>
              ))}
            </Select>
            </div>
          <div name="adnCharge">
                    <label>Name</label>
                    <select name="adnCharge" onChange={onChangeUserForm}>                
                        <option>Select</option>
                        {personName.map((item,index) => <option key={index}>{item}</option>)}
                    </select>  
          </div>
          <div name="desgnSuffix" >
                    <label>Suffix</label>
                    <select name="desgnSuffix" onChange={onChangeUserForm}>                
                        <option>Select</option>
                        {desigSuffix.map((item,index) => <option key={index}>{item}</option>)}
                    </select>  
          </div> 
          <button type="button" onClick={(e)=>addWithSuffix(e)}>Add</button>
          <div>{finalValue.map((item, index)=><Chip key={index} label={item}/>)}</div>   
          </form>
        </>
      )
    }
      
      const container = document.getElementById('root');
    const root = ReactDOM.createRoot(container);
    root.render(<EditUsers />);
    <!DOCTYPE html>
    <html>
      <head>
        <script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
        <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
        <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
        <script src="https://unpkg.com/@mui/material@latest/umd/material-ui.production.min.js"></script>
      </head>
      <body>
    
        <div id="root"></div>
    
        <script type="text/babel" src="./script.js"></script>
      </body>
    </html>