ngxs-labs / firestore-plugin

Firestore plugin for NGXS
https://ngxs-firebase-plugin.netlify.com/
MIT License
20 stars 14 forks source link

How to use NGXS updateItem() with Firebase #46

Closed santekotturi closed 3 years ago

santekotturi commented 3 years ago

When I update a document with update$, the update comes back into the app through the collection$ connection. But this has the one negative side effect of forcing me to update the entire state array with the updated payload which can cause flickering inside ngFors even when using trackBy.

Is there a way to access which document(s) were updated in the collection and then patch those using NGXS' patchItem?

https://www.ngxs.io/advanced/operators#supplied-state-operators

In my case, I'm currently updating all chatrooms with:

  @Action(StreamEmitted(FetchDirectChatrooms))
  streamEmitted(
    ctx: StateContext<DirectChatroomStateModel>,
    { action, payload }: Emitted<FetchDirectChatrooms, DirectChatroom[]>
  ) {

   ctx.patch({
       allChatrooms: payload.sort((a, b) => b.UPDATED_AT - a.UPDATED_AT
   })
  }

but I would love something like:

  @Action(StreamEmitted(FetchDirectChatrooms))
  streamEmitted(
    ctx: StateContext<DirectChatroomStateModel>,
    { action, payload }: Emitted<FetchDirectChatrooms, DirectChatroom[]>
  ) {

  const updatedId =  payload.metadata.updatedId;

    ctx.setState(
            patch({
              allChatrooms: updateItem< DirectChatroom >(
                (x) => x.id === updatedIds,
                patch< DirectChatroom >(x)
              )
            })
          );
  }
joaqcid commented 3 years ago

hi @skotturi

this is what i do in order to "upsert" items emitted by collection$

if (payload) {
  payload.forEach((directChatroom) => {
    ctx.setState(patch({ allChatrooms: upsertItem((x) => x.id === directChatroom.id, directChatroom) }));
  });
}

this way, you will only be adding new ones or updating those already in the store. the only caveat for this is that you would keep items that might be deleted in subsequent emissions

import { Predicate } from '@ngxs/store/operators/internals';
import { StateOperator } from '@ngxs/store';
import { compose, updateItem, iif, insertItem, patch } from '@ngxs/store/operators';

export function upsertItem<T>(selector: number | Predicate<T>, upsertValue: T): StateOperator<T[]> {
  return compose<T[]>(
    (foods) => <T[]>(foods || []),
    iif<T[]>(
      (foods) => Number(selector) === selector,
      iif<T[]>(
        (foods) => selector < foods.length,
        <StateOperator<T[]>>updateItem(selector, patch(upsertValue)),
        <StateOperator<T[]>>insertItem(upsertValue, <number>selector)
      ),
      iif<T[]>(
        (foods) => foods.some(<any>selector),
        <StateOperator<T[]>>updateItem(selector, patch(upsertValue)),
        <StateOperator<T[]>>insertItem(upsertValue)
      )
    )
  );
}
santekotturi commented 3 years ago

Thanks @joaqcid šŸ™šŸ¼