React map doesn't re-render as state changes

Viewed 1571

Could you help me with this please guys? I'm mapping this emails state but when I remove an email from the array in handleDelete the map doesn't re-render. When I console.log(emails) it's right, the email is removed correctly.

import { TextField, Chip, Avatar } from "@material-ui/core";
import React, { useState } from "react";
import "./Email.css";


export default function Email() {
   const [value, setValue] = useState<string>();
   const [emails, setEmails] = useState<string[]>([]);
   
   function onTextChange(e: any) {
      setValue(e.target.value.replace(" ", ""));
      if (value?.includes(",")) {
         let separated = e.target.value.split(",");
         const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
         const newEmails = separated.filter((val: any) => re.test(val));

         if (newEmails[0] !== undefined) {
            emails.push(newEmails[0]);
            setEmails(emails);
            console.log(emails);
         }

         setValue("");
      }
   }

   function handleDelete(email: string) {
      const index = emails.indexOf(email);
      if (index > -1) {
         setEmails(emails.splice(index, 1));
         console.log(email, "removed");
         console.log('new list',  emails)
      }
   }

   return (
      <div>
         <TextField
            value={value || ""}
            variant='outlined'
            onChange={onTextChange}
            id='textInput'
            InputProps={{
               startAdornment: (
                  <div style={{ top: "50%" }}>
                     {emails.map((email: any) => (
                        <Chip
                           label={email}
                           key={email}
                           avatar={<Avatar>{email[0].toUpperCase()}</Avatar>}
                           onDelete={() => handleDelete(email)}
                           style={{ display: "-webkit-inline-box" }}
                        />
                     ))}
                  </div>
               ),
            }}
         />
      </div>
   );
}
2 Answers

The issue is this line setEmails(emails.splice(index, 1)); Splice just changes the array in place so the memory location doesn't change for the array, so react doesnt see a change.

You want something like this setEmail(prevEmails => prevEmails.filter(e => e !== email))

Yes, I have met a similar problem before. First, it's because the "emails" variable is not changed. So I mean you call emails.splice function but the email variable not changed. You should create new array variable and set the changed value into it. Only that React state change listener can understand "emails" value changed so "I should render function again..".

function handleDelete(email: string) {
  const newEmails = [...emails];
  const index = newEmails.indexOf(email);
  if (index > -1) {
     setEmails(newEmails.splice(index, 1));
  }

}

Related