Home > Software design >  why are all my dialog screens showing at the same time? - ReactJS
why are all my dialog screens showing at the same time? - ReactJS

Time:12-23

first of all sorry if my question is unclear, it's my first time here (I just started learning web development four months ago haha).

Chronology: I'm making note-like web. Each note have edit and delete button, what i want is when i press edit button or delete button, one edit form or alert will be displayed and change will be applied to the specific note. The problem is, when i pressed edit button on one of my note, all dialog screen for all notes are being displayed and stacked like this: All dialog screen are being displayed and stacked

Anyone know why and how to fix it?

Here is the code (edited):

const [notes, setNotes] = useState([]);
const [title, setTitle] = useState('');
const [text, setText] = useState('');
const [newTitle, setNewTitle] = useState('');
const [newText, setNewText] = useState('');
const [open, setOpen] = useState(false);
const [openAlert, setOpenAlert] = useState(false);
const [openEdit, setOpenEdit] = useState(false);
const newNote = () => {
    Axios.post('http://localhost:8888/new', {
        title: title,
        text: text
    }).then((response) => {
        setNotes([...notes, response.data])
    })
};
const dataNotes = () => {
    Axios.get('http://localhost:8888/')
        .then((response) => {
            setNotes(response.data)
        })
};
const deleteNote = (id) => {
    Axios.delete(`http://localhost:8888/${id}`)
        .then((response) => {
            setNotes(notes.filter(note => note._id !== id))
        })
};
const editNote = (id) => {
    Axios.patch(`http://localhost:8888/${id}`, {
        title: newTitle,
        text: newText
    }).then((response) => {
        const newNotes = notes.map(note => {
            if (note._id === `${id}`) {
                return { ...note, title: newTitle, text: newText }
            }
            return note
        });
        setNotes(newNotes)
    })
}
const handleClickOpen = () => {
    setOpen(true);
};
const handleClose = () => {
    setOpen(false);
};
const handleClickOpenAlert = () => {
    setOpenAlert(true);
};
const handleCloseAlert = () => {
    setOpenAlert(false);
};
const handleClickOpenEdit = () => {
    setOpenEdit(true)
};
const handleCloseEdit = () => {
    setOpenEdit(false);
};
useEffect(() => {
    dataNotes();
}, []);
<section className='notes'>
    {notes.map((note) => (
        <div className="note-container" key={note._id}>
            <h3>
                {note.title}
            </h3>
            <p>
                {note.text}
            </p>
            <section className="tool-container">
                <img
                    src={PencilIcon}
                    alt='edit'
                    className="pencil"
                    onClick={handleClickOpenEdit}
                />
                <img
                    src={TrashIcon}
                    alt='delete'
                    className="trash"
                    onClick={handleClickOpenAlert}
                />
                <Dialog open={openAlert} onClose={handleCloseAlert} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
                    <DialogTitle id='alert-dialog-title'>{'Are you sure want to delete this note?'}</DialogTitle>
                    <DialogContent>
                        <DialogContentText id='alert-dialog-description'>
                            Once you delete this note, you can't get it back
                        </DialogContentText>
                    </DialogContent>
                    <DialogActions>
                        <Button onClick={handleCloseAlert}>Cancel</Button>
                        <Button onClick={(event) => {
                            deleteNote(note._id);
                            handleCloseAlert();
                        }}>Delete</Button>
                    </DialogActions>
                </Dialog>
                <Dialog open={openEdit} onClose={handleCloseEdit}>
                    <DialogTitle>Edit note</DialogTitle>
                    <DialogContent>
                        <TextField autoFocus type='text' id='new-title' label='New Note Title' fullWidth variant='standard' defaultValue={note.title} required onChange={(evt) => {
                            if (evt.target.value !== null) {
                                setNewTitle(evt.target.value)
                            } else {
                                setNewTitle(note.title)
                            }
                        }} />
                        <TextField type='text' id='new-text' label='New Note Text' fullWidth variant='standard' multiline defaultValue={note.text} required onChange={(evt) => {
                            if (evt.target.value !== null) {
                                setNewText(evt.target.value)
                            } else {
                                setNewText(note.text)
                            }
                        }} />
                    </DialogContent>
                    <DialogActions>
                        <Button onClick={handleCloseEdit}>Cancel</Button>
                        <Button onClick={() => {
                            editNote(note._id);
                            handleCloseEdit();
                        }}>Edit Note</Button>
                    </DialogActions>
                </Dialog>
            </section>
        </div>
    ))}
</section>

I think i'm missing something trivial here, but i don't know what it is.

CodePudding user response:

All your notes are sharing the same "open" state

This is why opening one note, opens them all.

const [openAlert, setOpenAlert] = useState(false);
const [openEdit, setOpenEdit] = useState(false);

I am not familiar with ReactJS but I am wondering whether something along these lines is possible. Instead of storing a single boolean in the state, store an object with an entry for each note. The keys in the object can be the note Ids. For simplicity you can also say that if a key is absent, the state is false. That way you can initialise with just an empty object.

const [openAlert, setOpenAlert] = useState({});
const [openEdit, setOpenEdit] = useState({});

Then your handler functions can be of this form:

const handleClickOpenEdit = (id) => {
  setOpenEdit(oldState => {
      ... oldState,
      [id]:true
  });
};

This means, when you click Open for Edit, expect to be passed the Id of the note, and the process of opening is as follows:

  1. Start with the old contents of the state variable.
  2. Add an entry (or replace it if it already exists) for the note Id, saying true.
  3. Return that as the new contents of the state variable.

You would call this handler with the note Id:

            <img
                src={PencilIcon}
                alt='edit'
                className="pencil"
                onClick={handleClickOpenEdit(note._id)}
            />
           

CodePudding user response:

Issue: All dialogs are using a single state.

Solution:
Make a seperate component for DialogBox

function DialogBox({note,title,content}) { 

   const [openAlert, setOpenAlert] = useState(false);
   const [openEdit, setOpenEdit] = useState(false);

   const handleClickOpenAlert = () => {
      setOpenAlert(true);
    }; 
   const handleCloseAlert = () => {
      setOpenAlert(false);
   };
   const handleClickOpenEdit = () => {
      setOpenEdit(true);
   };
   const handleCloseEdit = () => {
      setOpenEdit(false);
   }

    return (
        <Dialog open={openEdit} onClose={handleCloseEdit}>
            <DialogTitle>{title}</DialogTitle>
                <DialogContent>
                     {content}
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleCloseEdit}>Cancel</Button>
                    <Button onClick={() => {
                            editNote(note._id);
                            handleCloseEdit();
                           }}>Edit Note</Button>
                </DialogActions>
          </Dialog>
    );
}
export default DialogBox;

Use that component in main component

<section className='notes'>
    {notes.map((note) => (
        <div className="note-container" key={note._id}>
            <h3>
                {note.title}
            </h3>
            <p>
                {note.text}
            </p>
            <section className="tool-container">
                <img
                    src={PencilIcon}
                    alt='edit'
                    className="pencil"
                    onClick={handleClickOpenEdit}
                />
                <img
                    src={TrashIcon}
                    alt='delete'
                    className="trash"
                    onClick={handleClickOpenAlert}
                />
                <DialogBox 
                   title={"Are you sure want to delete this note?"}
                   content={"Once you delete this note, you can't get it back"}
                   note={note}
                />
                <DialogBox 
                   title={"Edit Note"}
                   content={<EditComponent/>}
                   note={note}
                />
</section>
  • Related