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:
- Start with the old contents of the state variable.
- Add an entry (or replace it if it already exists) for the note Id, saying
true
. - 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>