I have a React table which has a column containing delete buttons in each row. When a user clicks on the delete button, a modal pops up asking them to confirm deletion. Once user clicks confirm, the modal closes and the row should be deleted from the table.
I am trying to make it so that I don't have to refresh the page to see the updated table and instead see it right away once the confirm button in the modal is clicked and it closes.
Update: I tried the solution from the comments but I'm getting an error that says Uncaught TypeError: Cannot destructure property 'deleteCriteria' of '_ref' as it is undefined.
How would I fix this error? I'm very new with react so I'd appreciate any help!
This is my index.js file:
const DataTable = () => {
const dispatch = useDispatch();
const criterias = useSelector((state) => state.questionnaireData.criterias);
useEffect(() => {
if(criterias.length === 0) {
dispatch(fetchCriteria());
}
},[dispatch, criterias])
const mapCriterias = criterias.map((criterias) => ({
id: criterias.criteriaid,
criteria: criterias.criteria
}));
return (
<>
<table>
<tbody>
{criterias.map(criteria=> {
return (
<tr key={criteria.criteriaid}>
<td>{ criteria.id }</td>
<td>{ criteria.criteria }</td>
<td><DeleteButton deleteCriteria={dispatch(deleteCriteriaById)} id={criteria.criteriaid} /></td>
</tr>
)
</tbody>
</table>
</>
)
}
My renderDeleteButton (also in index.js):
export const DeleteButton = ({deleteCriteria, id}) => {
let dialogEl=null;
const delete_question = async () => {
try {
const response = await axios.delete(`http://localhost:3001/criteria/${encodeURIComponent(id)}`);
deleteCriteria(id);
} catch (err) {
console.error("delete_question", err.toJSON());
}
}
return (
<>
// modal
<dialog ref={(el) => {
dialogEl = el;
}}>
<div role="document">
<h2>Are you sure you would like to delete this food?</h2>
<p>This action cannot be undone</p>
<form method="dialog">
<div>
<div>
<button type="reset" onClick={()=>dialogEl.close()}>Cancel</button>
</div>
<div>
<button type="del" id="delete_bottom" onClick {()=>delete_question()}>Delete</button>
</div>
</div>
</form>
</div>
</dialog>
// delete button
<button onClick={() =>dialogEl.showModal()} className="delete-btn">
<span role="img">
<Icon icon="gg:trash-empty"/>
</span>
</button>
</>
)
}
My questionnaire.js file:
const initialState = {
criterias: [],
isFetching: false
}
const QuestionnaireSlice = createSlice({
name: "QUESTIONNNAIRE",
initialState,
reducers: {
fetchCriteria: (state) => ({...state, isFetching: true}),
confirmFetchCriteria: (state) => ({ ...state, isFetching: false }),
setCriteria: (state, action) => ({ ...state, criterias: action.payload }),
deleteCriteriaById: (state, action) => ({ ...state, criterias: state.criterias.filter(({criteriaid}) => criteriaid !== action.payload) })
}
})
CodePudding user response:
I think there may be a few optimizations you could make to help achieve what you want.
First I think you may be unnecessarily iterating over the criterias array twice - you should be able to just map once while rendering in your JSX return statement.
Second, your renderDeleteButton function looks like it wants to be a react component (it's returning JSX). So you'll want to implement this a little bit differently to allow the component to take in a delete handler and criteria ID as props.
The delete handler should be a dispatch function that updates your state with the new criterias array that does not include the one with that criteria id.
Something like:
const DeleteButton = ({id}) => {
const dispatch = useDispatch();
let dialogEl=null;
const delete_question = async () => {
try {
const response = await axios.delete(`http://localhost:3001/criteria/${encodeURIComponent(id)}`);
dispatch(deleteCriteriaById(id));
} catch (err) {
console.error("delete_question", err.toJSON());
}
}
return (
<>
// modal
<dialog ref={(el) => {
dialogEl = el;
}}>
<div role="document">
<h2>Are you sure you would like to delete this food?</h2>
<p>This action cannot be undone</p>
<form method="dialog">
<div>
<div>
<button type="reset" onClick={()=>dialogEl.close()}>Cancel</button>
</div>
<div>
<button type="del" id="delete_bottom" onClick={delete_question}>Delete</button>
</div>
</div>
</form>
</div>
</dialog>
// delete button
<button onClick={() =>dialogEl.showModal()} className="delete-btn">
<span role="img">
<Icon icon="gg:trash-empty"/>
</span>
</button>
</>
)
}
const DataTable = () => {
const dispatch = useDispatch();
const criterias = useSelector((state) => state.criterias);
useEffect(() => {
if(criterias.length === 0) {
dispatch(fetchCriteria());
}
},[dispatch, criterias])
return (
<>
<table>
<tbody>
{criterias.map(criteria=> {
return (
<tr key={criteria.criteriaid}>
<td>{ criteria.criteriaid }</td>
<td>{ criteria.criteria }</td>
<td><DeleteButton id={criteria.criteriaid} /></td>
</tr>
)})}
</tbody>
</table>
</>
)
}
deleteCriteriaById could probably look something like:
deleteCriteriaById: (state, action) => ({...state,
criterias: state.criterias.filter(({criteriaid}) => criteriaid !== action.payload)
}),
CodePudding user response:
Just change window.location.reload(false);
with dispatch(fetchCriteria());
assuming that the delete_question function is inside a React Functional Component.