function update()

in packages/relay-runtime/handlers/connection/ConnectionHandler.js [51:226]


function update(store: RecordSourceProxy, payload: HandleFieldPayload): void {
  const record = store.get(payload.dataID);
  if (!record) {
    return;
  }

  const {
    EDGES,
    END_CURSOR,
    HAS_NEXT_PAGE,
    HAS_PREV_PAGE,
    PAGE_INFO,
    PAGE_INFO_TYPE,
    START_CURSOR,
  } = ConnectionInterface.get();

  const serverConnection = record.getLinkedRecord(payload.fieldKey);
  const serverPageInfo =
    serverConnection && serverConnection.getLinkedRecord(PAGE_INFO);
  if (!serverConnection) {
    record.setValue(null, payload.handleKey);
    return;
  }
  // In rare cases the handleKey field may be unset even though the client
  // connection record exists, in this case new edges should still be merged
  // into the existing client connection record (and the field reset to point
  // to that record).
  const clientConnectionID = generateClientID(
    record.getDataID(),
    payload.handleKey,
  );
  const clientConnectionField = record.getLinkedRecord(payload.handleKey);
  const clientConnection =
    clientConnectionField ?? store.get(clientConnectionID);
  let clientPageInfo =
    clientConnection && clientConnection.getLinkedRecord(PAGE_INFO);
  if (!clientConnection) {
    // Initial fetch with data: copy fields from the server record
    const connection = store.create(
      clientConnectionID,
      serverConnection.getType(),
    );
    connection.setValue(0, NEXT_EDGE_INDEX);
    connection.copyFieldsFrom(serverConnection);
    let serverEdges = serverConnection.getLinkedRecords(EDGES);
    if (serverEdges) {
      serverEdges = serverEdges.map(edge =>
        buildConnectionEdge(store, connection, edge),
      );
      connection.setLinkedRecords(serverEdges, EDGES);
    }
    record.setLinkedRecord(connection, payload.handleKey);

    clientPageInfo = store.create(
      generateClientID(connection.getDataID(), PAGE_INFO),
      PAGE_INFO_TYPE,
    );
    clientPageInfo.setValue(false, HAS_NEXT_PAGE);
    clientPageInfo.setValue(false, HAS_PREV_PAGE);
    clientPageInfo.setValue(null, END_CURSOR);
    clientPageInfo.setValue(null, START_CURSOR);
    if (serverPageInfo) {
      clientPageInfo.copyFieldsFrom(serverPageInfo);
    }
    connection.setLinkedRecord(clientPageInfo, PAGE_INFO);
  } else {
    if (clientConnectionField == null) {
      // If the handleKey field was unset but the client connection record
      // existed, update the field to point to the record
      record.setLinkedRecord(clientConnection, payload.handleKey);
    }
    const connection = clientConnection;
    // Subsequent fetches:
    // - updated fields on the connection
    // - merge prev/next edges, de-duplicating by node id
    // - synthesize page info fields
    let serverEdges = serverConnection.getLinkedRecords(EDGES);
    if (serverEdges) {
      serverEdges = serverEdges.map(edge =>
        buildConnectionEdge(store, connection, edge),
      );
    }
    const prevEdges = connection.getLinkedRecords(EDGES);
    const prevPageInfo = connection.getLinkedRecord(PAGE_INFO);
    connection.copyFieldsFrom(serverConnection);
    // Reset EDGES and PAGE_INFO fields
    if (prevEdges) {
      connection.setLinkedRecords(prevEdges, EDGES);
    }
    if (prevPageInfo) {
      connection.setLinkedRecord(prevPageInfo, PAGE_INFO);
    }

    let nextEdges = [];
    const args = payload.args;
    if (prevEdges && serverEdges) {
      if (args.after != null) {
        // Forward pagination from the end of the connection: append edges
        if (
          clientPageInfo &&
          args.after === clientPageInfo.getValue(END_CURSOR)
        ) {
          const nodeIDs = new Set();
          mergeEdges(prevEdges, nextEdges, nodeIDs);
          mergeEdges(serverEdges, nextEdges, nodeIDs);
        } else {
          warning(
            false,
            'Relay: Unexpected after cursor `%s`, edges must ' +
              'be fetched from the end of the list (`%s`).',
            args.after,
            clientPageInfo && clientPageInfo.getValue(END_CURSOR),
          );
          return;
        }
      } else if (args.before != null) {
        // Backward pagination from the start of the connection: prepend edges
        if (
          clientPageInfo &&
          args.before === clientPageInfo.getValue(START_CURSOR)
        ) {
          const nodeIDs = new Set();
          mergeEdges(serverEdges, nextEdges, nodeIDs);
          mergeEdges(prevEdges, nextEdges, nodeIDs);
        } else {
          warning(
            false,
            'Relay: Unexpected before cursor `%s`, edges must ' +
              'be fetched from the beginning of the list (`%s`).',
            args.before,
            clientPageInfo && clientPageInfo.getValue(START_CURSOR),
          );
          return;
        }
      } else {
        // The connection was refetched from the beginning/end: replace edges
        nextEdges = serverEdges;
      }
    } else if (serverEdges) {
      nextEdges = serverEdges;
    } else {
      nextEdges = prevEdges;
    }
    // Update edges only if they were updated, the null check is
    // for Flow (prevEdges could be null).
    if (nextEdges != null && nextEdges !== prevEdges) {
      connection.setLinkedRecords(nextEdges, EDGES);
    }
    // Page info should be updated even if no new edge were returned.
    if (clientPageInfo && serverPageInfo) {
      if (args.after == null && args.before == null) {
        // The connection was refetched from the beginning/end: replace
        // page_info
        clientPageInfo.copyFieldsFrom(serverPageInfo);
      } else if (args.before != null || (args.after == null && args.last)) {
        clientPageInfo.setValue(
          !!serverPageInfo.getValue(HAS_PREV_PAGE),
          HAS_PREV_PAGE,
        );
        const startCursor = serverPageInfo.getValue(START_CURSOR);
        if (typeof startCursor === 'string') {
          clientPageInfo.setValue(startCursor, START_CURSOR);
        }
      } else if (args.after != null || (args.before == null && args.first)) {
        clientPageInfo.setValue(
          !!serverPageInfo.getValue(HAS_NEXT_PAGE),
          HAS_NEXT_PAGE,
        );
        const endCursor = serverPageInfo.getValue(END_CURSOR);
        if (typeof endCursor === 'string') {
          clientPageInfo.setValue(endCursor, END_CURSOR);
        }
      }
    }
  }
}