Person
Person

Reputation: 2259

How to react to fail response from server in React component if I need to setState after it?

I have a list of elements, each of them I can delete by clicking on them (click fires the call to the server, which sends back new list, without the deleted item).

But I need to visually delete them from the view before the response from the server arrives.

I do it like this

state = {
        items: this.props.items,
        deletedItem: null
    };

onClickDelete = (item) => {
        const {items} = this.state;
        const itemsCopy = {...items};

        for (const [key, value] of Object.entries(itemsCopy)) {
            if (value.id === tag.id) {
                delete itemsCopy[key];
                this.setState({
                    items: itemsCopy,
                    deletedItem: value
                });
            }
        }

        reduxActions(item)
    };

So that is happening:

My initial items are from props. When I click on the item I filter it out of state ( this way the item disappears from the view right away). Then I send action which performs a request to the server.

But is that the request to the server can return with an error. In that case, I want to return the deleted item to the state, so it will appear again.

Initially, I thought to create a flag in my Redux state, like itemDeleted: true. In case of successful request it stays true, in case of an error - it becomes false.

The problem as follows:

To react and redraw the component, adding an item, I'm doing something like this in my render method

if (!itemDeleted && !!deletedItem) { this.returnOnError(); }

And the function is

returnOnError= (): void => {
        const { deletedItem } = this.state;

        this.setState(prevState => ({
            items: {
                ...prevState.items,
                [deletedItem.id]: deletedItem
            }
        }));
    };

This causes two issues - one is that I'm calling setState in the render. Another is that when I click an item with error - the property itemDeleted becomes false and if I click again and get error again, the itemDeleted is already false so returnOnError fires anyway, without waiting for the result from the server.

I kind of lost there, all the help will be appreciated. The only restriction is that I need to delete the item before getting a response from the server.

Upvotes: 0

Views: 273

Answers (1)

SuleymanSah
SuleymanSah

Reputation: 17858

What you are trying to do is called "optimistic UI updates".

1-) Before deleting an item, save the original items.

2-) Update the UI immediately without waiting api call.

3-) In case of api error, recover the original items back.

You can try something like this:

handleDelete = async item => {
  /* store the current state in previousItems, just in case of server side fail */
  const previousItems = this.state.items;

  /* optimistically update on react side */
  const items = this.state.items.filter(p => p.id !== item.id);
  this.setState({ items });

  /*  If server side fail, rollback the state */
  try {
    await axios.delete(apiEndpoint + "/" + item.id);
  } catch (e) {
    this.setState({ items: previousItems });
  }
};

Here is a sample working codesandbox using react-redux to illustrate this.

Upvotes: 1

Related Questions