来自 useSelector 的数据导致无限重新渲染为依赖项
data from useSelector cause infinite re-render as a dependency
我正在使用 redux-thunk 做一个简单的 CRUD。
一切正常,我从商店获取数据,就像这样,我将其呈现在 table 组件中。
Table 组件摘录
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>
)
}
这是实际的 fetchdata 函数:
// 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"
});
}
};
};
但是,当我删除一项时,我希望 Table 组件重新呈现一次,显示新的 table,但缺少已删除的值。因此,当我使用数据配置 useEffect 时,它会无限地重新呈现。
useEffect(() => {
dispatch(getItems())
}, [dispatch,data])
我只希望组件渲染一次导致数据长度发生变化,但它什么都不做(数据?.长度),显示新的 tabla
我怎样才能避免这种情况?当然我可以使用某种页面重新加载,但似乎这不是解决方案。
我已经阅读了有关 memo、useMemo 和 useCallback 的信息,但我不知道如何在这种情况下配置它。
我正在添加减速器:
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;
}
}
以及删除操作:
//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"
});
}
};
};
在你的 sweetAlertConfirmDeleteItem
调度删除操作时,像这样调度它,
dispatch({
type: types.delete,
deletedItem: { id }
})
现在在你的 reducer 中你可以做到这一点,一旦你得到删除的项目,将它从数据中删除。
case types.delete:
return {
...state,
data: state.data.filter(item => item.id !== action.deletedItem.id),
deleted: action.deletedItem
}
同时从 useEffect 中删除 data
作为依赖项。
我所做的就是休闲
我将 UpdateItem.js 组件一分为二
one 从 useSelect 获取数据并渲染数据 if select!==null
另一个负责表格和接收道具
UpdateItem.js现在是这样的:
import axios from 'axios'
import React from 'react'
import { useForm } from '../hooks/useForm'
import { baseURL } from '../configuration/baseURL'
import { useDispatch } from 'react-redux';
import { types } from '../types/types';
import { useHistory } from 'react-router-dom'
import Swal from 'sweetalert2'
export default function UpdateItem({ id, name, cost, departmentName, departmentIdentification, categoryName, categoryId }) {
const history = useHistory()
const dispatch = useDispatch()
const [formValues, handleInputChange] = useForm({
newName: name,
newCost: cost,
newDepartmentName: departmentName,
newDepartmentIdentification: departmentIdentification,
newCategoryName: categoryName,
newCategoryId: categoryId
})
const {
newName,
newCost,
newDepartmentName,
newDepartmentIdentification,
newCategoryName,
newCategoryId } = formValues
const handleUpdateItem = async (e) => {
e.preventDefault()
try {
await axios.put(`${baseURL}${id}`, {
"id": +id,
"name": newName,
"cost": +newCost,
"department": [
{
"name": newDepartmentName,
"identification": newDepartmentIdentification
}
],
"category": [
{
"name": newCategoryName,
"id": +newCategoryId
}
]
})
const modified = await axios.get(`${baseURL}${id}`)
const { selected } = modified
dispatch({
type: types.modify,
modifiedItem: selected
});
Swal.fire({
icon: 'success',
title: 'Your item has been modified',
showConfirmButton: false,
timer: 1500
})
setTimeout(() => {
history.push('/')
}, 1500);
} catch (error) {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: 'Something went wrong!',
footer: 'Unable to modify item, who passes the id?'
})
return dispatch({
type: types.error,
msg: 'Unable to modify item'
})
}
}
return (
<div className='container mt-5 mb-5 pb-3 bg-light'>
<form className='mt-3' onSubmit={handleUpdateItem}>
<input
className='input mb-1 p-3'
type='text'
title='Name'
placeholder='Name'
name='newName'
autoComplete='off'
value={newName}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Cost'
placeholder='Cost'
name='newCost'
autoComplete='off'
value={newCost}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Department Name'
placeholder='Department Name'
name='newDepartmentName'
autoComplete='off'
value={newDepartmentName}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Department Identification'
placeholder='Department Identification'
name='newDepartmentIdentification'
autoComplete='off'
value={newDepartmentIdentification}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Category Name'
placeholder='Category Name'
name='newCategoryName'
autoComplete='off'
value={newCategoryName}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Category Id'
placeholder='Category Id'
name='newCategoryId'
autoComplete='off'
value={newCategoryId}
onChange={handleInputChange} />
<button className='btn btn-success ' type=' submit'>
Modify Item
</button>
</form>
</div>
)
}
和名为 ConditionalRenderUpdateItem.js 的新组件像这样
import React from 'react'
import { useSelector } from 'react-redux';
import UpdateItem from '../screen/UpdateItem';
export default function ConditionalRenderUpdateItem() {
const { selected } = useSelector(state => state.axiosDataReducer)
const id = selected?.id
const name = selected?.name
const cost = selected?.cost
const departmentName = selected?.department[0].name
const departmentIdentification = selected?.department[0].identification
const categoryName = selected?.category[0].name
const categoryId = selected?.category[0].id
return (
<div>
{(selected !== null) &&
<UpdateItem
id={id}
name={name}
cost={cost}
departmentName={departmentName}
departmentIdentification={departmentIdentification}
categoryName={categoryName}
categoryId={categoryId}
/>}
</div>
)
}
我正在使用 redux-thunk 做一个简单的 CRUD。
一切正常,我从商店获取数据,就像这样,我将其呈现在 table 组件中。
Table 组件摘录
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>
)
}
这是实际的 fetchdata 函数:
// 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"
});
}
};
};
但是,当我删除一项时,我希望 Table 组件重新呈现一次,显示新的 table,但缺少已删除的值。因此,当我使用数据配置 useEffect 时,它会无限地重新呈现。
useEffect(() => {
dispatch(getItems())
}, [dispatch,data])
我只希望组件渲染一次导致数据长度发生变化,但它什么都不做(数据?.长度),显示新的 tabla
我怎样才能避免这种情况?当然我可以使用某种页面重新加载,但似乎这不是解决方案。
我已经阅读了有关 memo、useMemo 和 useCallback 的信息,但我不知道如何在这种情况下配置它。
我正在添加减速器:
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;
}
}
以及删除操作:
//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"
});
}
};
};
在你的 sweetAlertConfirmDeleteItem
调度删除操作时,像这样调度它,
dispatch({
type: types.delete,
deletedItem: { id }
})
现在在你的 reducer 中你可以做到这一点,一旦你得到删除的项目,将它从数据中删除。
case types.delete:
return {
...state,
data: state.data.filter(item => item.id !== action.deletedItem.id),
deleted: action.deletedItem
}
同时从 useEffect 中删除 data
作为依赖项。
我所做的就是休闲
我将 UpdateItem.js 组件一分为二
one 从 useSelect 获取数据并渲染数据 if select!==null 另一个负责表格和接收道具
UpdateItem.js现在是这样的:
import axios from 'axios'
import React from 'react'
import { useForm } from '../hooks/useForm'
import { baseURL } from '../configuration/baseURL'
import { useDispatch } from 'react-redux';
import { types } from '../types/types';
import { useHistory } from 'react-router-dom'
import Swal from 'sweetalert2'
export default function UpdateItem({ id, name, cost, departmentName, departmentIdentification, categoryName, categoryId }) {
const history = useHistory()
const dispatch = useDispatch()
const [formValues, handleInputChange] = useForm({
newName: name,
newCost: cost,
newDepartmentName: departmentName,
newDepartmentIdentification: departmentIdentification,
newCategoryName: categoryName,
newCategoryId: categoryId
})
const {
newName,
newCost,
newDepartmentName,
newDepartmentIdentification,
newCategoryName,
newCategoryId } = formValues
const handleUpdateItem = async (e) => {
e.preventDefault()
try {
await axios.put(`${baseURL}${id}`, {
"id": +id,
"name": newName,
"cost": +newCost,
"department": [
{
"name": newDepartmentName,
"identification": newDepartmentIdentification
}
],
"category": [
{
"name": newCategoryName,
"id": +newCategoryId
}
]
})
const modified = await axios.get(`${baseURL}${id}`)
const { selected } = modified
dispatch({
type: types.modify,
modifiedItem: selected
});
Swal.fire({
icon: 'success',
title: 'Your item has been modified',
showConfirmButton: false,
timer: 1500
})
setTimeout(() => {
history.push('/')
}, 1500);
} catch (error) {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: 'Something went wrong!',
footer: 'Unable to modify item, who passes the id?'
})
return dispatch({
type: types.error,
msg: 'Unable to modify item'
})
}
}
return (
<div className='container mt-5 mb-5 pb-3 bg-light'>
<form className='mt-3' onSubmit={handleUpdateItem}>
<input
className='input mb-1 p-3'
type='text'
title='Name'
placeholder='Name'
name='newName'
autoComplete='off'
value={newName}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Cost'
placeholder='Cost'
name='newCost'
autoComplete='off'
value={newCost}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Department Name'
placeholder='Department Name'
name='newDepartmentName'
autoComplete='off'
value={newDepartmentName}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Department Identification'
placeholder='Department Identification'
name='newDepartmentIdentification'
autoComplete='off'
value={newDepartmentIdentification}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Category Name'
placeholder='Category Name'
name='newCategoryName'
autoComplete='off'
value={newCategoryName}
onChange={handleInputChange} />
<input
className='input mb-1 p-3'
type='text'
title='Category Id'
placeholder='Category Id'
name='newCategoryId'
autoComplete='off'
value={newCategoryId}
onChange={handleInputChange} />
<button className='btn btn-success ' type=' submit'>
Modify Item
</button>
</form>
</div>
)
}
和名为 ConditionalRenderUpdateItem.js 的新组件像这样
import React from 'react'
import { useSelector } from 'react-redux';
import UpdateItem from '../screen/UpdateItem';
export default function ConditionalRenderUpdateItem() {
const { selected } = useSelector(state => state.axiosDataReducer)
const id = selected?.id
const name = selected?.name
const cost = selected?.cost
const departmentName = selected?.department[0].name
const departmentIdentification = selected?.department[0].identification
const categoryName = selected?.category[0].name
const categoryId = selected?.category[0].id
return (
<div>
{(selected !== null) &&
<UpdateItem
id={id}
name={name}
cost={cost}
departmentName={departmentName}
departmentIdentification={departmentIdentification}
categoryName={categoryName}
categoryId={categoryId}
/>}
</div>
)
}