【问题标题】:How to build a Redux + ReduxToolkit store with a repeating nested slice?如何使用重复嵌套切片构建 Redux + ReduxToolkit 商店?
【发布时间】:2021-09-22 17:51:15
【问题描述】:

假设我的状态看起来像这样(取自https://redux.js.org/usage/structuring-reducers/normalizing-state-shape):

{
    posts : {
        byId : {
            "post1" : {
                id : "post1",
                author : "user1",
                body : "......",
                comments : ["comment1", "comment2"]
            },
            "post2" : {
                id : "post2",
                author : "user2",
                body : "......",
                comments : ["comment3", "comment4", "comment5"]
            }
        },
        allIds : ["post1", "post2"]
    },
    comments : {
        byId : {
            "comment1" : {
                id : "comment1",
                author : "user1",
                comment : ".....",
            },
            "comment2" : {
                id : "comment2",
                author : "user2",
                comment : ".....",
            }
        },
        allIds : ["comment1", "comment2"]
    },
    users : {
        byId : {
            "user1" : {
                username : "user1",
                name : "User 1",
            },
            "user2" : {
                username : "user2",
                name : "User 2",
            }
        },
        allIds : ["user1", "user2"]
    }
}

因此,我使用 Redux-Toolkit 的 createSlice 创建了 3 个切片:postsSlice、cmetsSlice、usersSlice。 这很简单。

但现在我想在状态中添加另一层来跟踪博客,所以上面的整个结构需要为每个博客嵌套和重复:

{
    blogs: {
        byId: {
            "blog1": {
                posts: {...},
                comments: {...},
                users: {...}
            },
            "blog2": {
                posts: {...},
                comments: {...},
                users: {...}
            },
        },
        allIds: ["blog1", "blog2"]
    }
}

现在我需要使用createSlice 创建一个新切片,blogsSlice。但是对于这个切片中的每个博客项目,我需要一个由其他 3 个切片组成的子状态。

如何组合切片/reducer 以实际创建此结构?我应该如何定义传递给createSlice 的初始状态和reducer 函数以使这个结构工作?

【问题讨论】:

    标签: redux redux-toolkit state-management


    【解决方案1】:

    我实际上整理了一个要点,展示了与此非常相似的东西,用于概念上的 ChatRoom > ChatMessages 结构。这可能不是您正在寻找的内容,但希望它能让您了解如何构建可作为灵感的事物。

    这里粘贴整个sn-p作为例子:

    // Example of using multiple / nested `createEntityAdapter` calls 
    // within a single Redux Toolkit slice
    
    interface Message {
      id: string;
      roomId: string;
      text: string;
      timestamp: string;
      username: string;
    }
    
    interface ChatRoomEntry {
      id: string;
      messages: EntityState<Message>;
    }
    
    const roomsAdapter = createEntityAdapter<ChatRoomEntry>();
    const messagesAdapter = createEntityAdapter<Message>();
    
    const fetchRooms = createAsyncThunk(
      "chats/fetchRooms",
      chatsAPI.fetchRooms
    );
    
    const fetchMessages = createAsyncThunk(
      "chats/fetchMessages",
      async (roomId) => {
        return chatsAPI.fetchMessages(roomId);
      }
    )
    
    const chatSlice = createSlice({
      name: "chats",
      initialState: roomsAdapter.getInitialState(),
      reducers: {
      
      },
      extraReducers: builder => {
        builder.addCase(fetchRooms.fulfilled, (state, action) => {
          const roomEntries = action.payload.map(room => {
            return {id: room.id, messages: messagesAdapter.getInitialState()};
          });
          
          roomsAdapter.setAll(state, roomEntries);
        })
        .addCase(fetchMessages.fulfilled, (state, action) => {
          const roomId = action.meta.arg;
          const roomEntry = state.entities[roomId];
          if (roomEntry) {
            messagesAdapter.setAll(roomEntry.messages, action.payload);
          }
        })
      }
    })
    
    /*
    Resulting state:
    {
      ids: ["chatRoom1"],
      entities: {
        chatRoom1: {
          id: "chatRoom1",
          messages: {
            ids: ["message1", "message2"],
            entities: {
              message1: {id: "message1", text: "hello"},
              message2: {id: "message2", text: "yo"},
            }
          }
        }
      }
    }
    */
    

    【讨论】:

    • 感谢您的回复。在我的示例中,为了简单起见,我使用了规范化状态,但我的实际状态并未规范化,实体的概念并不真正适用,因此使用 createEntityAdapter 对我来说不是一个解决方案,这就是我为每个创建切片的原因“实体”。在您的解决方案中,我可以使用从另一个切片调用 reducer 函数来替换适配器(例如 messagesAdapter.setAll)的使用吗?是否允许从其他减速器中调用减速器?如果是这样,它必须在 extraReducers 中还是可以在常规 reducer 中?
    • reducer 可以调用它需要的任何其他函数来完成它的工作,只要它们共同遵循 reducer 的规则。只要最终的外部可见结果是不可变的,reducer 甚至可以在其内部真正改变 new 值。所以可以肯定的是,reducer A 可以在自身内部调用 reducer B 来产生一个中间值作为计算过程的一部分。
    猜你喜欢
    • 2017-09-17
    • 2021-06-05
    • 2017-05-29
    • 2021-05-26
    • 1970-01-01
    • 2016-06-07
    • 2020-03-26
    • 2021-02-06
    • 2017-12-07
    相关资源
    最近更新 更多