来自 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>
    )
}