简体   繁体   English

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

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

I wrote simple reducer for User entity, and now I want to apply best practices for it, when switching action types and returning state. 我为User实体编写了简单的reducer,现在我想在切换操作类型和返回状态时为其应用最佳实践。 Just to mention, I extracted actions types in separate file, actionsTypes.js . 仅提及一下,我将动作类型提取到了单独的文件actionsTypes.js

Content of 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';

First question, is it mandatory to have actions types for FAILED case? 第一个问题,是否有针对FAILED案例的操作类型是强制性的? For example, to add GET_USERS_FAILED and so on and handle them inside usersReducer? 例如,要添加GET_USERS_FAILED等并在usersReducer中处理它们?

Root reducer is: 根减少剂是:

const rootReducer = combineReducers({
    users
});

There is code of usersReducer, and I put comments/questions inside code, and ask for answers (what are best practices to handle action types): 有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;
    }
}

I'm not an experienced developer but let me answer your questions what I've learned and encountered up to now. 我不是一位经验丰富的开发人员,但请允许我回答您到目前为止所学到的问题。

First question, is it mandatory to have actions types for FAILED case? 第一个问题,是否有针对失败案例的操作类型是强制性的? For example, to add GET_USERS_FAILED and so on and handle them inside usersReducer? 例如,要添加GET_USERS_FAILED等并在usersReducer中处理它们?

This is not mandatory but if you intend to give a feedback to your clients it would be good. 这不是强制性的,但是如果您打算向客户提供反馈,那就太好了。 For example, you initiated the GET_USERS process and it failed somehow. 例如,您启动了GET_USERS进程,但由于某种原因它失败了。 Nothing happens on client side, nothing updated etc. So, your client does not know it failed and wonders why nothing happened. 客户端没有任何反应,没有更新等。因此,您的客户端不知道它失败了,并且想知道为什么什么也没发生。 But, if you have a failure case and you catch the error, you can inform your client that there was an error. 但是,如果遇到故障案例并捕获到错误,则可以通知您的客户有错误。

To do this, you can consume GET_USERS_FAILED action type in two pleases for example. 为此,您可以使用两个示例来使用GET_USERS_FAILED操作类型。 One in your userReducers and one for, lets say, an error or feedback reducer. 您的userReducers一个,而说一个errorfeedback减少器则是一个。 First one returns state since your process failed and you can't get the desired data, hence does not want to mutate the state anyhow. 第一个返回状态,因为您的过程失败了,您无法获得所需的数据,因此无论如何都不想改变状态。 Second one updates your feedback reducer and can change a state, lets say error and you catch this state in your component and if error state is true you show a nice message to your client. 第二个更新您的反馈减少器并可以更改状态,可以说是error然后在组件中捕获该状态,如果error状态为true,则会向客户端显示一条好消息。

state of usersReducer is 'users' array, so I just return action.payload where it is array of users. usersReducer的状态是“用户”数组,所以我只返回action.payload,它是用户数组。 Will it automatically update users array on initial state? 它将在初始状态下自动更新用户数组吗?

case actionsTypes.GET_USERS_SUCCESS:
    return action.payload;

This is ok if you are fetching whole users with a single request. 如果您通过单个请求获取整个用户,则可以。 This means your action.payload which is an array becomes your state. 这意味着作为数组的action.payload成为您的状态。 But, if you don't want to fetch all the users with a single request, like pagination, this would be not enough. 但是,如果您不希望通过单个请求获取所有用户(例如分页),那还不够。 You need to concat your state with the fetched ones. 您需要与获取的状态保持一致。

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

Here, we are using spread syntax . 在这里,我们使用传播语法

It, obviously, spread what is given to it :) You can use it in a multiple ways for arrays and also objects. 显然,它可以扩展所提供的内容:)您可以以多种方式将其用于数组以及对象。 You can check the documentation. 您可以检查文档。 But here is some simple examples. 但是这里有一些简单的例子。

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

We spread arr in a new array and add 4 to it. 我们将arr散布到一个新数组中,并添加4。

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

Here, we spread our obj in a new object and changed its age property. 在这里,我们将obj散布到新对象中并更改了age属性。 In both examples, we never mutate our original data. 在两个示例中,我们都不会对原始数据进行突变。

What to return here? 这里要返回什么? Just action.payload where it is just single user object? 只是action.payload在哪里,它只是单个用户对象?

case actionsTypes.GET_USER_SUCCESS:
    return ;

Probably you can't use this action in this reducer directly. 可能您不能直接在此reducer中使用此操作。 Because your state here holds your users as an array. 因为这里的状态将您的用户保留为数组。 What do you want to do the user you got somehow? 您想对拥有某种方式的用户做什么? Lets say you want to hold a "selected" user. 假设您要保留“选定”用户。 Either you can create a separate reducer for that or change your state here, make it an object and hold a selectedUser property and update it with this. 您可以为此创建一个单独的reducer或在此处更改您的状态,使其成为对象并持有selectedUser属性并用此属性对其进行更新。 But if you change your state's shape, all the other reducer parts need to be changed since your state will be something like this: 但是,如果您更改状态的形状,则所有其他异径管零件都需要更改,因为您的状态将是这样的:

{
    users: [],
    selectedUser,
}

Now, your state is not an array anymore, it is an object. 现在,您的状态不再是数组,而是一个对象。 All your code must be changed according to that. 您的所有代码都必须根据该代码进行更改。

what does this mean? 这是什么意思? Can someone explain this code? 有人可以解释此代码吗? It returns new array, but what about spread operator, and object.assign? 它返回新数组,但是散布运算符和object.assign呢?

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

I've already tried to explain spread syntax. 我已经尝试解释传播语法。 Object.assign copies some values to a target or updates it or merges two of them. Object.assign将一些值复制到目标或对其进行更新或合并其中两个。 What does this code do? 该代码的作用是什么?

First it takes your state, filters it and returns the users not equal to your action.payload one, which is the user is being added. 首先,它获取您的状态,对其进行过滤,并返回与您的action.payload不相等的用户,即正在添加的用户。 This returns an array, so it spreads it and merges it with the Object.assign part. 这将返回一个数组,因此将其展开并将其与Object.assign部分合并。 In Object.assign part it takes an empty object and merges it with the user. 在Object.assign部分中,它需要一个空对象并将其与用户合并。 An all those values creates a new array which is your new state. 所有这些值将创建一个新数组,这是您的新状态。 Let's say your state is like: 假设您的状态如下:

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

and your new user is: 您的新用户是:

{
     id: 3, name: "baz"
}

Here what this code does. 这是这段代码的作用。 First it filters all the user and since filter criteria does not match it returns all your users (state) then spread it (don't forget, filter returns an array and we spread this array into another one): 首先,它过滤所有用户,并且由于过滤条件不匹配,它返回所有用户(状态),然后将其散布(别忘了,过滤器返回一个数组,然后我们将该数组散布到另一个数组中):

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

Now the Object.assign part does its job and merges an empty object with action.payload, a user object. 现在,Object.assign部分完成其工作,并将空对象与用户对象action.payload合并。 Now our final array will be like this: 现在我们的最终数组将如下所示:

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

But, actually Object.assign is not needed here. 但是,实际上,这里不需要Object.assign。 Why do we bother merging our object with an empty one again? 为什么我们还要麻烦地将对象与空对象合并? So, this code does the same job: 因此,此代码执行相同的工作:

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

is this ok? 这个可以吗?

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

It seems ok to me. 对我来说似乎还可以。 You don't mutate the state directly, use spread syntax to create a new one, update the related part and finally set your state with this new one. 您无需直接更改状态,使用扩展语法来创建新的状态,更新相关部分,并最终使用此新状态来设置状态。

I'm not sure about this delete part, is this ok or there is best practice to return state without deleted user? 我不确定这个删除部分,可以吗?还是有一种最佳做法是在不删除用户的情况下返回状态?

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

Again, it seems ok to me. 再次,对我来说似乎还可以。 You filter the deleted user and update your state according to that. 您可以过滤已删除的用户并根据该状态更新您的状态。 Of course there are other situations you should take into considiration . 当然,还有其他情况需要考虑。 For example do you have a backend process for those? 例如,您是否有后端流程? Do you add or delete users to a database? 您是否向数据库添加或删除用户? If yes for all the parts you need to sure about the backend process success and after that you need to update your state. 如果所有部分都为是,则需要确保后端过程成功,然后需要更新状态。 But this is a different topic I guess. 但这是一个不同的话题。

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

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