javascript - Using Redux Toolkit's createEntityAdapter for dynamic data grids - Stack Overflow

I have a question about using createEntityAdapter from Redux Toolkit.I have an application that shows

I have a question about using createEntityAdapter from Redux Toolkit.

I have an application that shows packages and package details using the AG Grid library's master/detail functionality. The app loads the package data in first, then fetches the detail data as each package row is expanded.

I would like to use createEntityAdapter to manage the data in a normalized way, but I'm having trouble figuring out how to handle the dynamic nature of the detail grids. Right now I have a reducer slice that creates a new property every time a set of detail records is loaded. The property key is the id of the parent package row, so my data slice ends up looking something like this after the package data and the 123 detail data has been loaded:

{
  PACKAGES: [{ id: 123, ...otherData }, { id: 124, ...otherData }],
  '123': [{ id: 456, ...otherDetailData }],
}

When the user expands the 124 package row, the detail data for that package is fetched and then it looks like this:

{
  PACKAGES: [{ id: 123, ...otherData }, { id: 124, ...otherData }],
  '123': [{ id: 456, ...otherDetailData }],
  '124': [{ id: 457, ...otherDetailData }],
}

So while I could break out the PACKAGES data into its own entity adapter, I can't do the same for the detail grids because I don't know about each of them ahead of time.

I think I'm having a similar problem as the person from this issue.

I thought about storing all the detail data in a single entity adapter and maintaining another index by the parent id, but that seems difficult to keep in sync.

Any ideas? What is the best practice in this scenario?

I have a question about using createEntityAdapter from Redux Toolkit.

I have an application that shows packages and package details using the AG Grid library's master/detail functionality. The app loads the package data in first, then fetches the detail data as each package row is expanded.

I would like to use createEntityAdapter to manage the data in a normalized way, but I'm having trouble figuring out how to handle the dynamic nature of the detail grids. Right now I have a reducer slice that creates a new property every time a set of detail records is loaded. The property key is the id of the parent package row, so my data slice ends up looking something like this after the package data and the 123 detail data has been loaded:

{
  PACKAGES: [{ id: 123, ...otherData }, { id: 124, ...otherData }],
  '123': [{ id: 456, ...otherDetailData }],
}

When the user expands the 124 package row, the detail data for that package is fetched and then it looks like this:

{
  PACKAGES: [{ id: 123, ...otherData }, { id: 124, ...otherData }],
  '123': [{ id: 456, ...otherDetailData }],
  '124': [{ id: 457, ...otherDetailData }],
}

So while I could break out the PACKAGES data into its own entity adapter, I can't do the same for the detail grids because I don't know about each of them ahead of time.

I think I'm having a similar problem as the person from this issue.

I thought about storing all the detail data in a single entity adapter and maintaining another index by the parent id, but that seems difficult to keep in sync.

Any ideas? What is the best practice in this scenario?

Share Improve this question asked Feb 28, 2021 at 18:21 Jake BooneJake Boone 1,3902 gold badges13 silver badges26 bronze badges 2
  • I'm not familiar with the AG Grid data types. If a "package" and a "detail" are separate entities then it might make sense to handle them separately (could be two properties in the same slice). If it's an issue of lazily-loaded data then you can restructure each entity into something more custom like an object with properties meta and details. I could give you more concrete suggestions if I knew the data structures. Do you have a repo or a CodeSandbox that you can share? – Linda Paiste Commented Mar 1, 2021 at 0:38
  • @LindaPaiste "Package" isn't specific to AG Grid, it's just the data type I'm dealing with. Maybe think of it like shipping packages (boxes) and the details are the contents of each box. The list of boxes is loaded first (I can do that with an entity adapter), then as the user clicks on each box, the contents of that box get fetched and loaded. It's the second part I'm having trouble mapping to an entity adapter. Hope that helps. – Jake Boone Commented Mar 1, 2021 at 4:18
Add a ment  | 

1 Answer 1

Reset to default 5

I created and maintain Redux Toolkit, and I actually did do something kinda similar recently with nested entity adapters.

In my case, I needed to maintain these auxiliary data sets as parent items were added or removed.

interface ChildrenEntry {
  parentId: ParentId;
  children: EntityState<ChildType>
}

interface ChildrenData {
  parentId: ParentId;
  children: ChildType[];
}

interface NewChildAdded {
  parentId: ParentId;
  child: ChildType;
}

export const childEntriesAdapter = createEntityAdapter<ChildrenEntry>();
export const childrenAdapter = createEntityAdapter<ChildType>();

const initialState = childEntriesAdapter.getInitialState();

const createNewChildEntry = (parentId: ParentId) => ({
  parentId,
  children: childrenAdapter.getInitialState()
});

const childEntriesSlice = createSlice({
  name: "children",
  initialState,
  reducers: {
    childEntriesLoaded(state, action: PayloadAction<ChildrenData>) {
      const {parentId, children} = action.payload;
      const childEntry = state.entities[parentId];
      if (childEntry) {
        childrenAdapter.setAll(childEntry.children, children);
      }
    },
    // etc
  },
  extraReducers: builder => {
    builder
      .addCase(parentLoaded, (state, action) => {
        const childEntries = action.payload.map(parent => createNewChildEntry(parent.id));
        childEntriesAdapter.setAll(state, childEntries);
      })
      .addCase(parentDeleted, (state, action) => {
        childEntriesAdapter.removeOne(state, action);
      })
      .addCase(parentAdded, (state, action) => {
        const childEntry = createNewChildEntry(action.payload.id);
        childEntriesAdapter.addOne(state, childEntry);
      });
  }
})

There might be a better way to handle this, but it worked sufficiently well for my needs.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745655993a4638545.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信