Клиент Apollo: оптимистичные обновления в процессе создания

Я хочу иметь возможность обновлять объект, пока он еще создается.

Например: скажем, у меня есть список дел, в который я могу добавить элементы с именами. Я также хочу иметь возможность редактировать названия предметов.

Теперь, скажем, пользователь с медленным соединением создает элемент. В этом случае я запускаю мутацию создания элемента и оптимистично обновляю свой интерфейс. Это прекрасно работает. Пока проблем нет

Теперь предположим, что мутация создания элемента занимает немного времени из-за медленной сети. В это время пользователь решает изменить имя элемента, который он только что создал. Для идеального опыта:

  1. Пользовательский интерфейс должен немедленно обновить с новым именем
  2. Новое имя должно быть сохранено на сервере

Я могу достичь #2, ожидая завершения создания мутации (чтобы я мог получить идентификатор элемента), а затем сделав мутацию имени обновления. Но это означает, что части моего пользовательского интерфейса будут оставаться неизменными до тех пор, пока не вернется мутация создания элемента и не начнется оптимистический ответ мутации имени обновления. Это означает, что № 1 не будет достигнут.

Поэтому мне интересно, как я могу достичь и #1 и #2 с помощью клиента Apollo.

Примечание: я не хочу добавлять счетчики или отключать редактирование. Я хочу, чтобы приложение чувствовало себя отзывчивым даже при медленном соединении.

2 ответа

Если у вас есть доступ к серверу, вы можете реализовать upsert операции, так что вы можете свести все запросы к такому:

mutation { upsertTodoItem( where: { key: $itemKey # some unique key generated on client } update: { listId: $listId text: $itemText } create: { key: $itemKey listId: $listId text: $itemText } ) { id key } }

Таким образом, у вас будет последовательность идентичных мутаций, отличающихся только переменными. Оптимистический ответ, соответственно, может быть настроен на эту одну мутацию. На сервере нужно проверить, есть ли товар с таким key уже существует и соответственно создайте или обновите элемент

Кроме того, вы можете использовать https://github.com/helfer/apollo-link-debounce, чтобы уменьшить количество запросов, когда пользователь печатает

Я думаю, что самый простой способ достичь желаемого эффекта - это отказаться от оптимистичных обновлений в пользу самостоятельного управления состоянием компонента. На данный момент у меня нет полосы пропускания, чтобы написать полный пример, но ваша базовая структура компонентов будет выглядеть так:

<ApolloConsumer>
  {(client) => (
    <Mutation mutation={CREATE_MUTATION}>
      {(create) => (
        <Mutation mutation={EDIT_MUTATION}>
          {(edit) => (
            <Form />
          )}
        </Mutation>        
      )}
    </Mutation>
  )}
</ApolloConsumer>

Давайте предположим, что мы имеем дело только с одним полем - name, Ваш Form Компонент будет начинаться с начального состояния

{ name: '', created: null, updates: null }

После отправки форма будет делать что-то вроде:

onCreate () {
  this.props.create({ variables: { name: this.state.name } })
    .then(({ data, errors }) => {
      // handle errors whichever way
      this.setState({ created: data.created })
      if (this.state.updates) {
        const id = data.created.id
        this.props.update({ variables: { ...this.state.updates, id } })
      }
    })
    .catch(errorHandler)
}

Тогда логика редактирования выглядит примерно так:

onEdit () {
  if (this.state.created) {
    const id = this.state.created.id
    this.props.update({ variables: { name: this.state.name, id } })
      .then(({ data, errors }) => {
        this.setState({ updates: null })
      })
      .catch(errorHandler)
  } else {
    this.setState({ updates: { name: this.state.name } })
  }
}

По сути, ваша мутация редактирования либо запускается немедленно, когда пользователь отправляет (так как мы уже получили ответ от нашей мутации создания)... или изменения, сделанные пользователем, сохраняются и затем отправляются после завершения создания мутации.

Это очень грубый пример, но он должен дать вам представление о том, как справиться с подобным сценарием. Самым большим недостатком является то, что существует вероятность того, что состояние вашего компонента не синхронизируется с кешем - вам нужно убедиться, что вы правильно обрабатываете ошибки, чтобы этого избежать.

Это также означает, что если вы хотите использовать эту форму только для редактирования, вам нужно извлечь данные из кэша, а затем использовать его для заполнения вашего начального состояния (т.е. this.state.created в приведенном выше примере). Вы можете использовать Query компонент для этого, просто убедитесь, что вы не рендер Form компонент, пока у вас нет data опора предоставлена Query составная часть.

Другие вопросы по тегам