skip to Main Content

When i am trying to add a new contact it is throwing Maximum call stack size exceeded

This is my App.js


import { useEffect, useState } from 'react';
import { AddContact } from './AddContact';

import { ContactList } from './ContactList';
import { Header } from './Header';
import { uuid } from 'uuidv4';

function App() {
  const objKey ='contacts'
  const [contacts,setContacts]=useState([]);

  const detailsToParent = (contact) => {
    const newContact = { id: uuid(), ...contact };
    const updatedContacts = [...contacts, newContact];
    setContacts(updatedContacts); // Update the state first
    localStorage.setItem(objKey, JSON.stringify(updatedContacts));
  };
  
  
  useEffect(() => {
    const localArray = JSON.parse(localStorage.getItem(objKey));
    if (localArray) setContacts(localArray);
  }, []);

  
  const deleteContactHandler = (id) => {
    const remainingContacts = contacts.filter((contact) => contact.id !== id); 
    setContacts(remainingContacts);
  };
  

  return ( <div>
    <Header/>
    <AddContact detailsToParent={detailsToParent}/>
    <ContactList contacts={contacts} getContactId={deleteContactHandler} />
     
  </div>)
}

export default App;

this is my contactList.js


import { Delete } from '@mui/icons-material';
import { Avatar } from '@mui/material';
import React from 'react';

export const ContactList = (props) => {
  const list = props.contacts.map((contact) => {
    return (
      <div
        className='d-flex shadow m-2 rounded p-4 bg-light justify-content-between align-items-end'
      >
        <div className='d-flex'>
          <Avatar className='me-4' style={{ color: 'blue' }} />
          <div>
            <div>
              <b>{contact.name}</b>
            </div>
            <div>{contact.email}</div>
          </div>
        </div>
        <Delete
          onClick={() => props.getContactId(contact.id) }
          key={contact.id}
          style={{ color: 'red' }}
        />
      </div>
    );
  });

  return (
    <div>
      <h5 className='ms-4 mt-4'>Contacts</h5>
      {list}
    </div>
  );
};

This is my AddContact.js

import React from 'react'

export const AddContact = (props) => {
  const details={
  };
  
  function add(e){
    e.preventDefault();
     var name = document.getElementById('name').value;
     var email = document.getElementById('email').value;
     details.name=name
     details.email=email
     document.getElementById('name').value=document.getElementById('name').defaultValue
     document.getElementById('email').value=document.getElementById('email').defaultValue
    if (details.name==='' || details.email===''){
      alert('all the fields are mandatory!');
      return
    }
    props.detailsToParent(details);
  }
  return (
    <>
    <div className='ps-3'>
      
      <div className='mt-2 fs-2 fw-bold'>
        Add Contact
      </div>
      <form  onSubmit={(e) => add(e)}>
        <div>
          <label for='name' className='m-1 fw-bold'>Name </label><br/>
          <input
          type='text'
          placeholder='name' 
          name='name' 
          id='name'
          className='m-1 h-2 b-0 form-control'/>
        </div>
        <div>
          <label for='email' className='m-1 fw-bold'>Email</label> <br/>    
          <input type='email' placeholder='[email protected]' name='email' className='m-1 b-0 form-control' id='email'/>
        </div>
        <input type='submit' value='add'  className='btn btn-primary p-2 mt-2 px-5'/>
      </form>
    </div>
    </>
  )
}

I am expecting to add a new contact when i click on the add button. initially it worked well but i changed my code to delete the contact by clicking on the delete icon then it started to throw error.

2

Answers


  1. Your AddContact component is missing a key concept of React: state. Try using setState like:

    export const AddContact = ({detailsToParent}) => {
      const [name, setName] = React.useState("");
      const [email, setEmail] = React.useState("");
      
      function add(e) {
        e.preventDefault();
        detailsToParent({name, email});
      }
    
      // ...
        <input
          value={name}
          onChange={({target: {value}}) => setName(value)}
          name="name"
        >
        <input
          value={email}
          onChange={({target: {value}}) => setEmail(value)}
          name="email"
        >
      // ...
    };
    

    Runnable example:

    const AddContact = ({onAdd}) => {
      const [name, setName] = React.useState("");
      const [email, setEmail] = React.useState("");
    
      const handleAdd = (e) => {
        e.preventDefault();
        onAdd({name, email});
      };
    
      return (
        <div>
          <form onSubmit={handleAdd}>
            <input
              value={name}
              onChange={({target: {value}}) => setName(value)}
            />
            <input
              value={email}
              onChange={({target: {value}}) => setEmail(value)}
            />
            <input type="submit" />
          </form>
        </div>
      );
    };
    
    const ContactList = ({contacts, onDelete}) => {
      const list = contacts.map((contact) => (
        <div key={contact.id}>
          <div>
            <div>
              <div>
                <b>{contact.name}</b>
              </div>
              <div>{contact.email}</div>
            </div>
          </div>
          <button onClick={() => onDelete(contact.id)}>X</button>
        </div>
      ));
    
      return (
        <div>
          <h5>Contacts</h5>
          {list}
        </div>
      );
    };
    
    const App = () => {
      const [contacts, setContacts] = React.useState([]);
    
      const handleDelete = (id) => {
        setContacts((prev) => prev.filter((e) => e.id !== id));
      };
    
      const handleAdd = ({name, email}) => {
        setContacts((prev) => [
          ...prev,
          {
            name,
            email,
            id: crypto.randomUUID(),
          },
        ]);
      };
    
      return (
        <div>
          <AddContact onAdd={handleAdd} />
          <ContactList contacts={contacts} onDelete={handleDelete} />
        </div>
      );
    };
    
    ReactDOM.createRoot(document.querySelector("#app")).render(<App />);
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div id="app"></div>

    In general, avoid document.querySelector. Avoiding direct DOM access is part of the point of React: you delcaratively design the UI and the React engine handles manipulating the DOM for you. Only bypass it rarely, with good reason, and generally use refs when you do.

    Same for reassignments–almost all component-oriented data goes through immutable state hooks. It’s good practice to treat non-state variables as immutable too.

    By the way,

    setContacts(updatedContacts); // Update the state first
    localStorage.setItem(objKey, JSON.stringify(updatedContacts));
    

    has a misleading comment. State updates are async and can only be read on the next render, so it’s good practice to move state setters to the end of functions so you’re not misled into thinking you can read the new value immediately. Luckily, in this case, you’re referring to updatedContacts, not contacts on the localStorage line, so it’s good. But remove the comment and switch order to avoid confusion:

    localStorage.setItem(objKey, JSON.stringify(updatedContacts));
    
    // do it last so you're not tempted to use `contacts` under the new value
    setContacts(updatedContacts);
    
    Login or Signup to reply.
  2. The "Maximum call stack size exceeded" error from what i saw occurs because there is an issue with the way you are updating the state in your detailsToParent function. When you add a new contact, you are using the spread operator to create a new array called updatedContacts, but you are not correctly updating the state with this new array.

    Here’s how you can fix the issue:

    const detailsToParent = (contact) => {
      const newContact = { id: uuid(), ...contact };
      const updatedContacts = [...contacts, newContact];
      setContacts(updatedContacts); // Update the state first
      localStorage.setItem(objKey, JSON.stringify(updatedContacts));
    };

    In the above code, you are using the previous state (contacts) to create a new array (updatedContacts). This is the correct way to update state in React when the new state depends on the previous state.

    However, when you call setContacts(updatedContacts), React will re-render your component, and the useEffect hook will be triggered. In your useEffect hook, you are parsing the local storage and setting the state again, which causes an infinite loop, leading to the "Maximum call stack size exceeded" error.

    To fix this issue, you can update your useEffect to only run once when the component mounts by passing an empty dependency array:

    useEffect(() => {
      const localArray = JSON.parse(localStorage.getItem(objKey));
      if (localArray) setContacts(localArray);
    }, []);

    With this change, the useEffect will only run when the component first mounts, and subsequent updates to the contacts state won’t trigger it again.

    I hope this is useful bro.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search