Клиент Apollo: оптимистичные обновления в процессе создания - PullRequest
0 голосов
/ 07 ноября 2018

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

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

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

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

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

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

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

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

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

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

<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 } })
  }
}

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

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

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

0 голосов
/ 13 ноября 2018

Если у вас есть доступ к серверу, вы можете выполнить 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 и создать или обновить элемент соответственно.

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

...