Mutations

Relay 分开以下API以执行 mutation。

const {commitMutation} = require('react-relay');

type Variables = {[name: string]: any};

commitMutation(
  environment: Environment,
  config: {
    mutation: GraphQLTaggedNode,
    variables: Variables,
    onCompleted?: ?(response: ?Object, errors: ?[Error]) => void,
    onError?: ?(error: Error) => void,
    optimisticResponse?: Object,
    optimisticUpdater?: ?(store: RecordSourceSelectorProxy) => void,
    updater?: ?(store: RecordSourceSelectorProxy, data: SelectorData) => void,
    configs?: Array<RelayMutationConfig>,
  },
);

首先,我们来看看 environment 输入。 要 environment 使用相关数据正确执行 mutation,最好使用 environment 用于渲染的组件。在 this.props.relay.environment 可以从组件访问。

现在我们来看一下 config:

  • mutation: graphql 标记的 mutation 查询。
  • variables:包含 mutation 所需变量的对象。
  • onCompleted: 使用“原始”响应执行的回调函数和内存 Relay 存储之后服务器的错误updater.
  • onError: 当 Relay 遇到错误时执行的回调函数。
  • optimisticResponse: 符合 mutation 的响应类型定义的对象。如果提供,optimistic response 则在 optimisticUpdater 执行之前被标准化到代理存储,我们建议你提供 optimisticResponse 因为有两个好处:
    • updater 类似, 不需要为了简单的 mutation (字段变更) 去提供optimisticUpdater
    • 对于更复杂的 mutation, optimisticUpdaterupdater 可以是同一个函数
  • optimisticUpdater: 一个接收内存 Relay 存储代理的功能。在这个功能中,客户端定义了如何通过代理以一种必要的方式更新存储。
  • updater: 根据实际服务器响应更新内存 Relay 存储的功能。当服务器响应返回时,Relay 首先恢复由 optimisticUpdateroptimisticResponse 造成的更改,然后在应用 updater 到存储。
  • configs: 包含不同的optimisticUpdater/updater配置的数组。它提供了一种方便的方式来指定updater行为。

示例 #

一个简单的 mutation, 你只需要 mutation变量:

const {
  commitMutation,
  graphql,
} = require('react-relay');

const mutation = graphql`
  mutation MarkReadNotificationMutation(
    $input: MarkReadNotificationData!
  ) {
    markReadNotification(data: $input) {
      notification {
        seenState
      }
    }
  }
`;

function markNotificationAsRead(environment, source, storyID) {
  const variables = {
    input: {
      source,
      storyID,
    },
  };

  commitMutation(
    environment,
    {
      mutation,
      variables,
      onCompleted: (response, errors) => {
        console.log('Response received from server.')
      },
      onError: err => console.error(err),
    },
  );
}

积极地更新客户端 #

为了提升交互体验,你可能会希望执行「积极更新」,客户端即使在服务器的响应回来之前也能立即更新以反映预期的新值。我们通过提供一个optimisticResponse并把它加到我们传进commitMutation config 中来完成它:

const optimisticResponse = {
  markReadNotification: {
    notification: {
      seenState: SEEN,
    },
  },
};

commitMutation(
  environment,
  {
    mutation,
    optimisticResponse,
    variables,
  },
);

配置 #

我们可以以配置数组的形式给出 Relay 指令,说明如何使用每个 mutation 的响应来更新客户端存储。我们通过配置以下一种或多种以下突变类型的 mutation 来实现:

NODE_DELETE #

给定一个deletedIDFieldName,Relay将从连接中删除节点。

参数 #

  • deletedIDFieldName: string: 包含已删除节点的DataID的响应中的字段名称

示例 #

const mutation = graphql`
  mutation DestroyShipMutation($input: DestroyShipData!) {
    destroyShip(input: $input) {
      destroyedShipId
      faction {
        ships {
          id
        }
      }
    }
  }
`;

const configs = [{
  type: 'NODE_DELETE',
  deletedIDFieldName: 'destroyedShipId',
}];

RANGE_ADD #

给定一个父级,关于连接的信息以及响应负载 Relay 将创建节点添加到存储中,并根据连接信息中指定的范围行为将其附加到连接。

参数 #

  • parentID: string: 包含连接父节点的DataID。
  • connectionInfo: Array<{key: string, filters?: Variables, rangeBehavior: string}>:包含连接密钥的对象数组,包含可选过滤器的对象,以及取决于我们期望的行为(附加,前缀或忽略)的范围行为。
    • filters: 包含GraphQL调用的对象,例如 const filters = {'orderby': 'chronological'};.
  • edgeName: string: 表示新创建的边的响应中的字段名称

示例 #

const mutation = graphql`
  mutation AddShipMutation($input: AddShipData!) {
    addShip(input: $input) {
      faction {
        ships {
          id
        }
      }
      newShipEdge
    }
  }
`;

const configs = [{
  type: 'RANGE_ADD',
  parentID: 'shipId',
  connectionInfo: [{
    key: 'AddShip_ships',
    rangeBehavior: 'append',
  }],
  edgeName: 'newShipEdge',
}];

RANGE_DELETE #

给定父节点,connectionKeys,响应有效负载中的一个或多个DataID以及父节点和连接之间的路径,Relay将从连接中删除节点,但将关联的记录保留在存储中。

参数 #

  • parentID: string: 包含连接的父节点的DataID。
  • connectionKeys: Array<{key: string, filters?: Variables}>: 包含连接密钥和可选过滤器的对象数组。
    • filters: 包含GraphQL调用的对象,例如 const filters = {'orderby': 'chronological'};.
  • pathToConnection: Array<string>: 包含父级和连接之间的字段名称的数组,包括父级和连接。
  • deletedIDFieldName: string | Array<string>: 响应中包含已删除节点的DataID的字段名称,或从连接中删除节点的路径

示例 #

const mutation = graphql`
  mutation RemoveTagsMutation($input: RemoveTagsData!) {
    removeTags(input: $input) {
      todo {
        tags {
          id
        }
      }
      removedTagId
    }
  }
`;

const configs = [{
  type: 'RANGE_DELETE',
  parentID: 'todoId',
  connectionKeys: [{
    key: RemoveTags_tags,
    rangeBehavior: 'append',
  }],
  pathToConnection: ['todo', 'tags'],
  deletedIDFieldName: removedTagId
}];

有关更复杂的积极更新的示例,包括从列表中添加和删除,请参阅 Relay Modern Todo 示例应用程序。

以程序方式更新存储(高级) #

当优化更新需要更精细的控制时,Relay 存储可以在高级边缘情况下以编程方式进行 mutation。以下API方法可用于突破连接和 fragment。

RelayRecordStore #

getSource(): RecordSource #

返回存储所有记录的内部RecordSource的只读视图。

getRootField(fieldName: string): ?RecordProxy #

返回一个用于处理来自记录源的记录的代理类,例如查询,变更或存储。

RelayRecordProxy #

getDataID(): DataID #

返回记录的全局唯一标识符字符串。

getType(): RelayQLType #

返回给定记录的GraphQL类型名称。

getValue(name: string, args?: ?Variables): mixed #

通过字段名称读取记录上属性的值,并读取表示预定义参数值的对象。

setValue(value: mixed, name: string, args?: ?Variables): RecordProxy #

更新由字段名称给出的可变记录属性的值,以及表示预定义参数值的对象。

getLinkedRecord(name: string, args?: ?Variables): ?RecordProxy #

getLinkedRecords(name: string, args?: ?Variables): ?Array<?RecordProxy> #

检索与给定记录关联的记录,通过字段名称传递source,以及表示预定义参数值的对象。

setLinkedRecord(record: RecordProxy, name: string, args?: ?Variables): RecordProxy #

setLinkedRecords(records: Array<?RecordProxy>, name: string, args?: ?Variables ): RecordProxy #

更新与可变记录关联的记录,通过字段名称传递source,以及表示预定义参数值的对象。

getOrCreateLinkedRecord(name: string, typeName: string, args?: ?Variables ): RecordProxy #

查找或创建与可变记录关联的单个记录。这是RelayRecordProxy.getLinkedRecordRelayRecordProxy.setLinkedRecord的一个快捷方式,如果关联的记录是不存在的。

高级 Mutation 示例 #

const sharedUpdater = (source, todoItem) => {
    const sourceRecord = source.getRootField('todoList');

    const todoItems = sourceRecord.getLinkedRecords('todoItems');
    if (todoItems) {
      sourceRecord.setLinkedRecords(todoItems.concat(todoItem), 'todoItems');
    }
};

const variables = {
  todoItem: {
    task: 'Finish this example!',
    dueDate: null,
  }
}

commitMutation(store, {
  mutation,
  variables,
  updater: (store) => {
    const mutationRoot = store.getRootField('addTodoItem');
    const todoItem = mutationRoot.getLinkedRecord('todoItem');
    sharedUpdater(store, todoItem);
  },
  optimisticUpdater: (store) => {
    const todoItem = mutationRoot.getLinkedRecord('todoItem');
    sharedUpdater(store, variables.todoItem);
  }
});