0

Im running a react app with Firebase in the back. I have an endpoint "/items".

When I invoke a DELETE request through Axios, the database reflects the correct data. However, after the DELETE request when I invoke a GET request to retrieve updated "items" I get a null reference where the deleted item was, leading to a null reference error.

EXAMPLE
-Original [item1, item2, item3]
*Delete item2

-Get
   - [item1, null, item3]

However on Firebase it is reflected correctly..

class VendingMachine extends Component {
    state = {
        balance: "",
        items: [],
    }

getItems = () => {
    axios.get('items.json')
        .then(response => {
            if (response.data) {
                this.setState({ items: Object.values(response.data) });
            }
        })

}


deleteItemHandler = (id) => {
    axios.delete('/items/' + id + '.json')
        .then((response) => {
            this.getItems();
        });

}

Put Request is in another component..

class NewItem extends Component {

    errorElement = React.createRef();

    state = {
        newItem: {
            name: "",
            position: null,
            price: "",
            image: ""
        },
        showError: false
    };

    addItemHandler = () => {
        if (this.props.items.length === 9) {
            this.setState({ showError: true });
            return;
        }
        const newItem = {
            id: this.props.items.length,
            name: this.state.newItem.name,
            price: this.state.newItem.price,
            image: this.state.newItem.image,
            position: this.props.items.length

        }
        console.log(newItem);
        axios.put('/items/' + newItem.id + '.json', newItem)
            .then(response => {
                this.props.updateItems()
            })
            .catch(error => console.log(error));
    }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Pbb
  • 408
  • 6
  • 21

1 Answers1

1

This is the expected behavior when you store arrays in Firebase.

When you store:

["item1", "item2", "item3"]

The Firebase Database server actually stores it as:

{
  "0": "item1",
  "1": "item2",
  "2": "item3"
}

And then means that when you delete item2, you end up with:

{
  "0": "item1",
  null,
  "2": "item3"
}

Which the Firebase client then translates back to:

["item1", null, "item3"]

You have two options:

  1. Always update the entire array
  2. Use a set instead of an array

Always update the entire array

If you want to keep storing the items as an array, you will have to renumber the index for all items after the ones you remove. So that means that any update to an array, means you need to read-and-then-write the entire array.


Use a set instead of an array

But you may not actually be storing an array. Many developers use arrays to store set-like data structures, i.e. unordered collections of unique items. For example: does ["item3", "item2", "item1"] have the same meaning at ["item1", "item2", "item3"]? And is ["item1", "item1", "item2", "item3"] actually illegal in your app? If so, you're looking at a set-like data structure, which in Firebase is better mapped to:

{
  "item1": true,
  "item2": true,
  "item3": true
}

The true value is meaningless and only needed because Firebase won't store a key without a value. But aside from that, this is the best structure to store such a set-like collection. And if you remove item2 from this, Firebase will simply return the object without that key:

{
  "item1": true,
  "item2": true,
  "item3": true
}

For more on this, also see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • should of just stuck to spring lol.. oh well, using a POST/PUT for a delete doesnt sound very restful.. – Pbb Jan 11 '19 at 01:08
  • You're deleting `/items/1`, which is the property that gets deleted. But you want other nodes to get updated, which is why you need to perform an update on a higher level. – Frank van Puffelen Jan 11 '19 at 01:17
  • makes sense when looking at it on a higher level – Pbb Jan 11 '19 at 01:30