I am doing a simple CRUD using redux-thunk.
Everything works fine, I obtain data from store ,like this and I render it in a table component.
Table component excerpt
import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getItems } from '../actions/actions'
import ButtonCreate from '../components/buttons/ButtonCreate'
import ButtonDelete from '../components/buttons/ButtonDelete'
import ButtonUpdate from '../components/buttons/ButtonUpdate'
export const Table = () => {
const dispatch = useDispatch()
const { data } = useSelector(state => state.axiosDataReducer)
console.log(data)
useEffect(() => {
dispatch(getItems())
}, [dispatch])
return (
<div className='container mt-5 mb-5'>
<ButtonCreate />
<table className="table table-striped table-hover caption-top ">
<caption>Online Store</caption>
<thead className='table-dark'>
<tr className='text-center'>
<th scope="col">Id</th>
<th scope="col">Name</th>
<th scope="col">Cost</th>
<th scope="col">Category</th>
<th scope="col">Department</th>
<th scope="col">Update</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
{
data?.map((x, index) => (
<tr key={x.id}>
<th scope="col">{index + 1}</th>
<th scope="col">{x.name}</th>
<th scope="col">$ {x.cost?.toFixed(2)}</th>
<th className='text-center'>
{
x.category.map((y, index) => (
<span key={index * 0.125}>{y.name}</span>
))
}
</th>
<th className='text-center'>
{
x.department.map((z, index) => (
<span key={index * 0.225}>{z.name}</span>
))
}
</th>
<th><ButtonUpdate id={x.id} /></th>
<th><ButtonDelete id={x.id} /></th>
</tr>
))
}
</tbody>
</table>
</div>
)
}
This is the fetchdata function in action:
// get items
export const getItems = () => {
return async (dispatch) => {
try {
const response = await axios.get(baseURL);
const data = response.data;
dispatch({
type: types.get,
data: data
});
} catch (error) {
return dispatch(
{
type: types.error,
msg: "Unable to get items"
});
}
};
};
But when I delete one item I want that the Table component re-render one time showing the new table with the lack of the deleted value. So, when I configure the useEffect with data it re-renders infinitely.
useEffect(() => {
dispatch(getItems())
}, [dispatch,data])
I just want that the component render one time cause the length of the data changes but it doesn t do anything (data?.length), showing the new tabla
How can I avoid this? of course I can use a sort of page reloading but it seems this is not the solution.
I have read about memo, useMemo and useCallback but I do not know how to configure it in this case scenario.
I am adding the reducer:
import { types } from "../types/types";
const initialState = {
data: null,
selected: null,
deleted: '',
created: null,
modified: null,
error: ''
}
export const axiosDataReducer = (state = initialState, action) => {
switch (action.type) {
case types.get:
return {
...state,
data: action.data
}
case types.selected:
return {
...state,
selected: action.selectedItem
}
case types.delete:
return {
...state,
deleted: action.deletedItem
}
case types.created:
return {
...state,
created: action.createdItem
}
case types.modified:
return {
...state,
modified: action.modifiedItem
}
case types.error:
return {
...state,
error: action.msg
}
default:
return state;
}
}
and also the actions for deleting:
//delete item
export const selectItem = (id) => {
return async (dispatch) => {
try {
const response = await axios.get(`${baseURL}${id}`);
const data = response.data;
dispatch({
type: types.selected,
selectedItem: data
});
} catch (error) {
return dispatch(
{
type: types.error,
msg: "Unable to select item for delete"
});
}
};
}
const sweetAlertConfirmDeleteItem = (id, dispatch) => {
Swal.fire({
title: 'Are you sure?',
text: "You won't be able to revert this!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!'
})
.then((result) => {
if (result.isConfirmed) {
axios.delete(`${baseURL}${id}`);
dispatch({
type: types.delete,
deletedItem: 'Item deleted'
})
Swal.fire(
'Deleted!',
'Your file has been deleted.',
'success'
)
}
})
}
export const getItemDeleteGetItems = (id) => {
return async (dispatch) => {
try {
dispatch(selectItem(id))
sweetAlertConfirmDeleteItem(id, dispatch)
} catch (error) {
return dispatch(
{
type: types.error,
msg: "Unable to delete item"
});
}
};
};
In your sweetAlertConfirmDeleteItem
when dispatching the delete action dispatch it like this ,
dispatch({
type: types.delete,
deletedItem: { id }
})
Now in your reducer you can do this, once you get the deleted item , remove it from the data.
case types.delete:
return {
...state,
data: state.data.filter(item => item.id !== action.deletedItem.id),
deleted: action.deletedItem
}
Also remove the data
as a dependency from the useEffect .