1

描述

我有一个在文件系统上嵌套了分页的容器。我最近在我的中继环境中添加了一个缓存层,以减少我们发出的请求数量。打开文件夹会触发 queryRender,该查询一次获取 8 条边,直到文件夹完全加载。在第一次提取时打开文件夹会按预期加载所有边缘。关闭文件夹并打开它只加载前 8 个边缘,并在第二个请求时失败。

我收到此错误:index.js:1 警告:RelayConnectionHandler:在光标“MTo3”之后出现意外,必须从列表末尾(“Mjow”)获取边。

添加{ force: true }解决了这个问题,但它会导致重新获取已经请求和存储的数据。

另一种方法是跟踪加载的文件计数,当第一次加载后触发 QueryRenderer 时,我可以传入加载的文件计数。

我只是想知道是否有更清洁的方法来实现我想要的?或者中继不支持我们的模式,我认为它应该的方式?

图片

首次加载成功

第一次加载得到 9 个边 失败第二次加载

第二次加载无法从缓存中分页

代码

文件浏览器.js

文件浏览器的根查询


    export default createPaginationContainer(
      FileBrowser,
      {
        repository: graphql`
          fragment FileBrowser_repository on Project {
            files(
              first: $first,
              after: $cursor
            ) @connection(
              key: "FileBrowser_files",
              filters: [$cursor]
            ) @skip (if: $fileSkip) {
                edges {
                  node {

                    ... on ProjectFile {
                      id
                      namespace
                      repositoryName
                      key
                      modifiedAt
                      sizeBytes
                      downloadURL
                    }

                    ... on ProjectDirectory {
                      id
                      key
                    }
                 }
              }

              pageInfo{
                endCursor
                hasNextPage
                hasPreviousPage
                startCursor
              }
            }
            __typename
          }`,
      },
      {
        direction: 'forward',
        getConnectionFromProps(props) {
          return props.repository && props.repository.files;
        },
        getFragmentVariables(prevVars, first) {
          return {
            ...prevVars,
            first,
          };
        },
        getVariables(props, { count, cursor }, fragmentVariables) {
          const { namespace, repositoryName } = props;
          return {
            ...fragmentVariables,
            first: count,
            cursor,
            repositoryName,
            namespace,
            filter: [],
          };
        },
        query: graphql`
           query FileBrowserQuery(
             $namespace: String!,
             $repositoryName: String!,
             $first: Int!,enter code here
             $cursor: String,
             $fileSkip: Boolean!
           ) {
             repository(
               namespace: $namespace,
               repositoryName: $repositoryName
             ) {
               ...FileBrowser_repository
             }
           }`,

      },
    );

文件夹查询.js

文件夹的分页容器


    // @flow
    // vendor
    import React, { Component } from 'react';
    import {
      createPaginationContainer,
      graphql,
    } from 'react-relay';
    // components
    import Folder from './Folder';
    // assets
    import './Folder.scss';


    const FolderPaginationContainer = createPaginationContainer(
      Folder,
      {
        contents: graphql`
        fragment Folder_contents on ProjectDirectory @relay(mask: false) {
          id
          key
          contents(
            first: $first,
            after: $cursor,
          ) @connection(
            key: "Folder_contents",
            filters: []
          ) {
            edges {
                node {
                  ... on ProjectFile {
                    id
                    namespace
                    repositoryName
                    key
                    modifiedAt
                    sizeBytes
                  }
                  ... on ProjectDirectory {
                    id
                    key
                  }
               }

               cursor
            }

            pageInfo {
              endCursor
              hasNextPage
              hasPreviousPage
              startCursor
            }
          }
        }`,
      },
      {
        direction: 'forward',
        getConnectionFromProps(props) {
          return props.node && props.node.contents;
        },
        getFragmentVariables(prevVars, first) {
          return {
            ...prevVars,
            first,
          };
        },
        getVariables(props, { count, cursor }, fragmentVariables) {
          const { id } = props.node;
          console.log(props, fragmentVariables);
          return {
            ...fragmentVariables,
            id,
            first: count,
            cursor,
            filter: [],
          };
        },
        query: graphql`
           query FolderQuery(
             $id: ID!,
             $first: Int!,
             $cursor: String,
           ) {
             node(
               id: $id,
             ) {
               ... on ProjectDirectory {
                 id
                 key
                 namespace
                 repositoryName
                 ...Folder_contents
               }
             }
           }`,
      },
    );

    export default FolderPaginationContainer;

FolderSection.js


    // @flow
    // vendor
    import React, { Component, Fragment } from 'react';
    // components
    import File from './File';
    import RefetchFolder from './RefetchFolder';



    type Props = {
      namespace: string,
      repositoryName: string,
      sort: string,
      reverse: bool,
      index: number,
      files: {
        edges: Array<Object>,
      },
      query: Object,
      __typename: string,
      skipSort: boolean,
      linkedDatasets: Object,
    }

    class FolderSection extends Component<Props> {
      render() {
        const {
          namespace,
          repositoryName,
          sort,
          reverse,
          index,
          files,
          __typename,
          query,
          skipSort,
          linkedDatasets,
        } = this.props;

        return (
          <Fragment>
            { files.map((edge) => {
              const {
                key,
              } = edge.node;
              const isDir = (edge.node.__typename === `${__typename}Directory`);
              const isFile = (edge.node.__typename === `${__typename}File`);

              if (isDir) {
                return (
                  <RefetchFolder
                    FolderSection={FolderSection}
                    name={key}
                    key={key}
                    edge={edge}
                    setState={this._setState}
                    rowStyle={{}}
                    sort={sort}
                    reverse={reverse}
                    rootFolder
                    index={index}
                    node={edge.node}
                    query={query}
                    __typename={__typename}
                    linkedDatasets={linkedDatasets}
                    namespace={namespace}
                    repositoryName={repositoryName}
                  />
                );
              }

              if (isFile) {
                return (
                  <File
                    name={key}
                    key={key}
                    edge={edge}
                    isExpanded
                    index={index}
                    __typename={__typename}
                  />
                );
              }

              return (
                <div>
                  Loading
                </div>
              );
            })}

          </Fragment>
        );
      }
    }

    export default FolderSection;

RefetchFolder.js

在 QueryRenderer 中请求每个文件夹的内容


    // @flow
    // vendor
    import React, { Component } from 'react';
    import uuidv4 from 'uuid/v4';
    import {
      QueryRenderer,
      graphql,
    } from 'react-relay';
    // environment
    import environment from 'JS/createRelayEnvironment';
    // component
    import Folder from './FolderProjectWrapper';

    type Props = {
      node: {
        id: String
      },
      relay: {
        refetch: Function,
      },
      edge: {
        node: Object
      },
      FolderSection: React.Node,
      index: Number,
      __typename: string,
      sort: string,
      linkedDatasets: Object,
      reverse: boolean,
    }


    const RefetchFolderQuery = graphql`query RefetchFolderQuery(
      $id: ID!,
      $first: Int!,
      $cursor: String,
    ) {
      node(
        id: $id,
      ) {
        ...Folder_contents @relay(mask: false)
      }
    }`;

    class RefetchFolder extends Component<Props> {
      state = {
        isExpanded: false,
        id: null,
        progress: 0,
        currentProgress: 0,
        step: 0.01,
      }


      static getDerivedStateFromProps(props, state) {
        const childNode = (props.node && props.node.contents)
          ? props.node 
          : props.edge.node;
        return {
          ...state,
          node: childNode,
        };
      }

      /**
      *  @param {Object} evt
      *  sets item to expanded
      *  @return {}
      */
      _expandFolder = () => {
        this.setState((state) => {
          const isExpanded = !state.isExpanded;
          const id = (state.id === null)
            ? uuidv4()
            : state.id;

          return {
            isExpanded,
            id,
          };
        });
      }


      render() {
        const {
          FolderSection,
          index,
          sort,
          reverse,
          linkedDatasets,
        } = this.props;
        const { isExpanded, node, progress } = this.state;
        const { __typename } = this.props;

        if (!isExpanded) {
          return (
            <Folder
              {...this.props}
              FolderSection={FolderSection}
              expandFolder={this._expandFolder}
              node={node}
              id={node.id}
              index={index}
              isExpanded={isExpanded}
              __typename={__typename}
            />
          );
        }
        return (
          <QueryRenderer
            query={RefetchFolderQuery}
            environment={environment}
            fetchPolicy="store-and-network"
            variables={{
              id: node.id,
              first: 8,
              cursor: null,
            }}
            render={({ props, error }) => {
              if (props) {
                const { hasNextPage } = props && props.node
                  && props.node.contents && props.node.contents.pageInfo;
                if (!hasNextPage) {
                  clearInterval(this.timeout);
                }
                return (
                  <Folder
                    FolderSection={FolderSection}
                    expandFolder={this._expandFolder}
                    node={props.node}
                    index={index}
                    isExpanded={isExpanded}
                    sort={sort}
                    reverse={reverse}
                    {...props}
                    __typename={__typename}
                    isLoading={hasNextPage}
                    progress={progress}
                    linkedDatasets={linkedDatasets}
                  />
                );
              }

              if (error) {
                return (
                  <div>Error</div>
                );
              }

              return (
                <Folder
                  FolderSection={FolderSection}
                  expandFolder={this._expandFolder}
                  node={node}
                  index={index}
                  isExpanded={isExpanded}
                  {...this.props}
                  __typename={__typename}
                  isLoading
                  progress={progress}
                  linkedDatasets={linkedDatasets}
                />
              );
            }}
          />
        );
      }
    }


    export default RefetchFolder;

文件夹.js


    // vendor
    import React, { Component } from 'react';
    import classNames from 'classnames';
    // ga
    import ga from 'JS/ga';
    // components
    import LinkedDataset from './LinkedDataset';
    // assets
    import './Folder.scss';


    type Props = {
      namespace: string,
      repositoryName: string,
      index: number,
      node: Object,
      relay: {
        refetchConnection: Function,
        environment: Object,
        loadMore: Function,
        isLoading: Function,
      },
      expandFolder: Function,
      isExpanded: Boolean,
      FolderSection: React.node,
      __typename: string,
    }

    class Folder extends Component<Props> {
      componentDidMount() {
        const { node } = this.props;
        if (node.contents && node.contents.pageInfo && node.contents.pageInfo.hasNextPage) {
          this._loadMore();
        }
      }


      componentDidUpdate() {
        const { node } = this.props;
        if (node.contents && node.contents.pageInfo && node.contents.pageInfo.hasNextPage) {
          this._loadMore();
        }
      }

      /**
        *  @param {string} key
        *  @param {string || boolean} value - updates key value in state
        *  update state of component for a given key value pair
        *  @return {}
        */
      _setState = (key, value) => {
        this.setState({ [key]: value });
      }

      /**
      * @param {}
      * refetches component looking for new edges to insert at the top of the activity feed
      * @return {}
      */
      _loadMore = () => {
        const { loadMore, isLoading } = this.props.relay;
        if (!isLoading()) {
          loadMore(
            8,
            () => {},
            // { force: true } commented out because it causes the cache to be ignored, but 
            // fixes the issue
          );
        }
      }

      /**
      *  @param {Object} evt
      *  sets item to expanded
      *  @return {}
      */
      _refetch = () => {
        const { refetchConnection } = this.props.relay;
        this.setState((state) => {
          const isExpanded = !state.isExpanded;
          return {
            isExpanded,
          };
        });

        refetchConnection(
          8,
          () => {
          },
          {
            id: this.props.node.id,
          },
        );
      }

      render() {
        const {
          namespace,
          repositoryName,
          index,
          node,
          expandFolder,
          isExpanded,
          FolderSection,
          __typename,
        } = this.props;
        const newIndex = index + 2;
        const { key } = node;
        const folderName = getName(key);
        // declare css here
        const folderRowCSS = classNames({
          Folder__row: true,
        });
        const folderChildCSS = classNames({
          Folder__child: true,
          hidden: !isExpanded,
        });
        const folderNameCSS = classNames({
          'Folder__cell Folder__cell--name': true,
          'Folder__cell--open': isExpanded,
        });

        return (
          <div className="Folder relative">
            <div
              className={folderRowCSS}
              onClick={evt => expandFolder(evt)}
              role="presentation"
            >
              <div className={folderNameCSS}>
                <div className="Folder__icon" />
                <div className="Folder__name">
                  {folderName}
                </div>
              </div>
              <div className="Folder__cell Folder__cell--size" />
              <div className="Folder__cell Folder__cell--date" />
            </div>
            <div className={folderChildCSS}>
              <FolderSection
                namespace={namespace}
                repositoryName={repositoryName}
                files={node.contents}
                node={node}
                index={newIndex}
                linkedDatasets={linkedDatasets}
                __typename={__typename}
              />
            </div>
          </div>
        );
      }
    }

    export default Folder;
4

0 回答 0