繁体   English   中英

React Redux-带CRUD的Reducer-最佳做法?

[英]React Redux - Reducer with CRUD - best practices?

我为User实体编写了简单的reducer,现在我想在切换操作类型和返回状态时为其应用最佳实践。 仅提及一下,我将动作类型提取到了单独的文件actionsTypes.js

actionsTypes.js内容:

export const GET_USERS_SUCCESS = 'GET_USERS_SUCCESS';
export const GET_USER_SUCCESS = 'GET_USER_SUCCESS';
export const ADD_USER_SUCCESS = 'ADD_USER_SUCCESS';
export const EDIT_USER_SUCCESS = 'EDIT_USER_SUCCESS';
export const DELETE_USER_SUCCESS = 'DELETE_USER_SUCCESS';

第一个问题,是否有针对FAILED案例的操作类型是强制性的? 例如,要添加GET_USERS_FAILED等并在usersReducer中处理它们?

根减少剂是:

const rootReducer = combineReducers({
    users
});

有usersReducer的代码,我在代码中放入了注释/问题,并寻求答案(处理操作类型的最佳实践是什么):

export default function usersReducer(state = initialState.users, action) {
    switch (action.type) {
        case actionsTypes.GET_USERS_SUCCESS:
        // state of usersReducer is 'users' array, so I just return action.payload where it is array of users. Will it automatically update users array on initial state?
            return action.payload;
        case actionsTypes.GET_USER_SUCCESS:
        // What to return here? Just action.payload where it is just single user object? 
            return ;
        case actionsTypes.ADD_USER_SUCCESS:
        // what does this mean? Can someone explain this code? It returns new array, but what about spread operator, and object.assign?
            return [...state.filter(user => user.id !== action.payload.id),
                Object.assign({}, action.payload)];
        case actionsTypes.EDIT_USER_SUCCESS:
        // is this ok?
            const indexOfUser = state.findIndex(user => user.id === action.payload.id);
            let newState = [...state];
            newState[indexOfUser] = action.payload;
            return newState;
        case actionsTypes.DELETE_USER_SUCCESS:
        // I'm not sure about this delete part, is this ok or there is best practice to return state without deleted user?
            return [...state.filter(user => user.id !== action.user.id)];
        default:
            return state;
    }
}

我不是一位经验丰富的开发人员,但请允许我回答您到目前为止所学到的问题。

第一个问题,是否有针对失败案例的操作类型是强制性的? 例如,要添加GET_USERS_FAILED等并在usersReducer中处理它们?

这不是强制性的,但是如果您打算向客户提供反馈,那就太好了。 例如,您启动了GET_USERS进程,但由于某种原因它失败了。 客户端没有任何反应,没有更新等。因此,您的客户端不知道它失败了,并且想知道为什么什么也没发生。 但是,如果遇到故障案例并捕获到错误,则可以通知您的客户有错误。

为此,您可以使用两个示例来使用GET_USERS_FAILED操作类型。 您的userReducers一个,而说一个errorfeedback减少器则是一个。 第一个返回状态,因为您的过程失败了,您无法获得所需的数据,因此无论如何都不想改变状态。 第二个更新您的反馈减少器并可以更改状态,可以说是error然后在组件中捕获该状态,如果error状态为true,则会向客户端显示一条好消息。

usersReducer的状态是“用户”数组,所以我只返回action.payload,它是用户数组。 它将在初始状态下自动更新用户数组吗?

case actionsTypes.GET_USERS_SUCCESS:
    return action.payload;

如果您通过单个请求获取整个用户,则可以。 这意味着作为数组的action.payload成为您的状态。 但是,如果您不希望通过单个请求获取所有用户(例如分页),那还不够。 您需要与获取的状态保持一致。

case actionsTypes.GET_USERS_SUCCESS:
    return [...state, ...action.payload];

在这里,我们使用传播语法

显然,它可以扩展所提供的内容:)您可以以多种方式将其用于数组以及对象。 您可以检查文档。 但是这里有一些简单的例子。

const arr = [ 1, 2, 3 ];
const newArr = [ ...arr, 4 ];
// newArr is now [ 1, 2, 3, 4 ]

我们将arr散布到一个新数组中,并添加4。

const obj = { id: 1, name: "foo, age: 25 };
const newObj = { ...obj, age: 30 };
// newObj is now { id: 1, name: "foo", age: 30 }

在这里,我们将obj散布到新对象中并更改了age属性。 在两个示例中,我们都不会对原始数据进行突变。

这里要返回什么? 只是action.payload在哪里,它只是单个用户对象?

case actionsTypes.GET_USER_SUCCESS:
    return ;

可能您不能直接在此reducer中使用此操作。 因为这里的状态将您的用户保留为数组。 您想对拥有某种方式的用户做什么? 假设您要保留“选定”用户。 您可以为此创建一个单独的reducer或在此处更改您的状态,使其成为对象并持有selectedUser属性并用此属性对其进行更新。 但是,如果您更改状态的形状,则所有其他异径管零件都需要更改,因为您的状态将是这样的:

{
    users: [],
    selectedUser,
}

现在,您的状态不再是数组,而是一个对象。 您的所有代码都必须根据该代码进行更改。

这是什么意思? 有人可以解释此代码吗? 它返回新数组,但是散布运算符和object.assign呢?

case actionsTypes.ADD_USER_SUCCESS:
    return [...state.filter(user => user.id !== action.payload.id), Object.assign({}, action.payload)];

我已经尝试解释传播语法。 Object.assign将一些值复制到目标或对其进行更新或合并其中两个。 该代码的作用是什么?

首先,它获取您的状态,对其进行过滤,并返回与您的action.payload不相等的用户,即正在添加的用户。 这将返回一个数组,因此将其展开并将其与Object.assign部分合并。 在Object.assign部分中,它需要一个空对象并将其与用户合并。 所有这些值将创建一个新数组,这是您的新状态。 假设您的状态如下:

[
    { id: 1, name: "foo" },
    { id: 2, name: "bar" },
] 

您的新用户是:

{
     id: 3, name: "baz"
}

这是这段代码的作用。 首先,它过滤所有用户,并且由于过滤条件不匹配,它返回所有用户(状态),然后将其散布(别忘了,过滤器返回一个数组,然后我们将该数组散布到另一个数组中):

[ { id: 1, name: "foo"}, { id: 2, name: "bar" } ]

现在,Object.assign部分完成其工作,并将空对象与用户对象action.payload合并。 现在我们的最终数组将如下所示:

[ { id: 1, name: "foo"}, { id: 2, name: "bar" }, { id: 3, name: "baz" } ]

但是,实际上,这里不需要Object.assign。 为什么我们还要麻烦地将对象与空对象合并? 因此,此代码执行相同的工作:

case actionsTypes.ADD_USER_SUCCESS:
    return [...state.filter(user => user.id !== action.payload.id), action.payload ];

这个可以吗?

case actionsTypes.EDIT_USER_SUCCESS:
    const indexOfUser = state.findIndex(user => user.id === action.payload.id);
    let newState = [...state];
    newState[indexOfUser] = action.payload;
    return newState;

对我来说似乎还可以。 您无需直接更改状态,使用扩展语法来创建新的状态,更新相关部分,并最终使用此新状态来设置状态。

我不确定这个删除部分,可以吗?还是有一种最佳做法是在不删除用户的情况下返回状态?

case actionsTypes.DELETE_USER_SUCCESS:
    return [...state.filter(user => user.id !== action.user.id)];

再次,对我来说似乎还可以。 您可以过滤已删除的用户并根据该状态更新您的状态。 当然,还有其他情况需要考虑。 例如,您是否有后端流程? 您是否向数据库添加或删除用户? 如果所有部分都为是,则需要确保后端过程成功,然后需要更新状态。 但这是一个不同的话题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM