CSFrequency / react-firebase-hooks

React Hooks for Firebase.
Apache License 2.0
3.58k stars 304 forks source link

Removing item from Realtime database causes all query items to added again at point where item removed #227

Open lachlanmc opened 2 years ago

lachlanmc commented 2 years ago

Hi, I'm probably just missing something obvious here:

If we have a list of widgets using something like

const [snapshot, loading, error] = useList(vQry);

{snapshot.map((item: { key: any; val: () => any }) => (
            <itemwidget>
              key={item.key}
              message={item.val()}
              ></itemwidget>
            ))}  

and then we remove an item from the realtime database using say:

fbdb.remove(Ref(database, "item/" + key))

The full query seems to be randomly appended/inserted at the point of the removed item rather than updating/overwriting existing items.

So if we had 100 itemwidget we now end up with 199 itemwidget and complaints about duplicate keys!

Sometimes it "works" about 20% of the time as expected in dev, more often if deployed to our test environment on firebase.

Adds and updates all seem to work as expected.

If we limit the list to say 50 and add an item in the middle somewhere, the last one is removed from the list as expected.

Tried various work arounds but feel like I've missed something glaringly obvious.

If I wasn't using useList but a standard array I'd filter then map. But even if we do this it doesn't solve the case where someone or something else removes the item...

Thoughts?

Thank you!

lachlanmc commented 2 years ago

OK, so even setting something crazy up like this to test then "snapshot" returns twice the number of items!!!

const [snapshot, loading, error] = useList( NotRunAfterDelete ? vQry : null);

NotRunAfterDelete is a useState that is set in the function that does the remove to force null to be passed to uselist

why??!?

lachlanmc commented 2 years ago

wasn't missing anything - seems to be an assumption on how something is going to work in the code.

copied the files into my project so I could debug

substituting the following has resolved the issue - if index is somehow < 1 then you seem to end up with state being updated with two copies of the array. the code is run multiple times during the change the first time correctly removes the item that was deleted. the second or third time you end up with a zero or a negative and hence you get the incorrect result set.

const removeChild = (
  currentState: KeyValueState,
  snapshot: DataSnapshot
): KeyValueState => {

  if (!snapshot.key) {
    return currentState;
  }

  console.log('removing item');

  const { keys, values } = currentState;
  const index = keys ? keys.indexOf(snapshot.key) : 0;

  if (index < 1) {
    return currentState;
  }

  const newkeys = keys ? [...keys.slice(0, index), ...keys.slice(index + 1)] : []
  const newvalues = values ? [...values.slice(0, index), ...values.slice(index + 1)] : []

  console.log('removed item');

  return {
    keys: newkeys,
    values: newvalues,
  };
};
lachlanmc commented 2 years ago

Probably a good assumption that all the other slice code in useListReducer will behave in an unpredictable fashion for index < 1

Comments?

seth-church commented 1 year ago

We're also seeing duplicate keys with useList. It's annoying having to check for and remove duplicates. Any ETA on this?

SikandAlex commented 1 year ago

Same

hkarn commented 6 months ago

I have the same issue and have to run a unique filtering on key before using the snapshot from useList..