首页 > 解决方案 > 来自 useSelector 的数据导致无限重新渲染为依赖项

问题描述

我正在使用 redux-thunk 做一个简单的 CRUD。

一切正常,我从 store 获取数据,就像这样,我将它呈现在一个表组件中。

表格组件摘录

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 组件重新渲染一次,以显示缺少已删除值的新表。因此,当我使用数据配置 useEffect 时,它会无限重新渲染。

useEffect(() => {
        dispatch(getItems())
    }, [dispatch,data])

我只想让组件渲染一次导致数据的长度发生变化,但它没有做任何事情(data?.length),显示新的表格

我怎样才能避免这种情况?当然,我可以使用某种页面重新加载,但似乎这不是解决方案。

我已经阅读了有关 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"
                });
        }
    };
};

标签: reactjsuse-effectredux-thunk

解决方案


在您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作为依赖项。


推荐阅读