Apollo本地状态-查询带有未知键的对象

时间:2019-06-18 22:59:55

标签: reactjs graphql apollo react-apollo apollo-client

我正在尝试将Apollo添加到应用中。我将使用它来获取数据,并希望遵循最佳实践并使用新的API(从Apollo 2.5开始)来存储本地UI状态。

我一直在研究these docs,但是它们的UI状态比我要实现的状态更简单。

我想移植我在Redux应用程序中一直使用的模式,其中许多UI元素的值都集中存储在其中。在Redux中,我有一个reducer对象,用于存储UI值,如下所示:

{
  dropdowns: {
    someDropdown: [{ index: 0, value: 'Selected Value' }]
  },
  checkboxes: {
    someCheckbox: true
  }
}

如图所示,不同类型的元素表示为对象,对于每个对象,每个键都是UI元素的唯一ID,并且值根据元素类型而变化。

我也喜欢上面的拼合版本,其中没有按UI元素类型的子分类。

我还有一个单独的reducer,用于存储某些UI元素的 state ,例如:

{
  openDropdownId: 'someDropdown',
  openModalId: null
}
  

至关重要的是,该数据似乎包含其键不可预测的对象。我既不确定如何使用gql进行键入,也不确定如何有效地获取此数据(请参阅问题底部)。

我曾尝试使用Apollo Client来重现此内容,但遇到了大量错误。这是我的客户端设置:

  

例如,对于一个下拉列表,想法是states在此处存储打开/关闭状态,而values存储有关所选值的信息。

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import gql from 'graphql-tag';

const typeDefs = gql`
  extend type UI {
    states: Object
    values: Object
  }
`;

const defaults = {
  ui: {
    __typename: 'UI',
    states: {},
    values: {}
  }
};

const cache = new InMemoryCache();
const client = new ApolloClient({
  cache,
  resolvers: {},
  typeDefs
});

console.log({ defaults });
cache.writeData({ data: defaults });

export default client;

然后我像这样使用该客户端:

<ApolloProvider client={client}>
  <App />
</ApolloProvider>

这是引发错误的查询示例:

const QUERY = gql`
  {
    ui @client {
      states
      values
    }
  }
`;

我看到的错误是:

  • Uncaught Invariant Violation: Missing selection set for object of type undefined returned for query field states

该错误显示在查询组件中。

我还收到两个警告(可能是states的警告和values的警告)

  • Missing field __typename in {}

我是否需要以不同的方式构造数据以查询这样的数据?


后续问题:如果组件具有UID,最好能够查询其值,例如:

const QUERY = gql`
  {
    ui @client {
      states {
        ${uid}
      }
      values {
        ${uid}
      }
    }
  }
`;

代替获取 all UI值,然后在组件中找到合适的UI值。

但是会引发错误:

Syntax Error: Invalid number, expected digit but got: "d".

是否有一种方法可以执行这样的查询,或者有更好的方法来处理所有这些问题?尽管此数据结构可能不是惯用的,但似乎Apollo应该为过滤查询支持此数据,不是吗?

1 个答案:

答案 0 :(得分:2)

我也曾在阿波罗进行地方状态管理的实验,因此以下内容有些人为设计且非常晦涩,但确实可行。

直言不讳地将所有非标量或数组上的东西都必须具有类型。

所以这个:

const defaults = {
  ui: {
    __typename: 'UI',
    states: {},
    values: {}
  }
};

需要成为例如:

const defaults = {
  ui: {
    __typename: 'UI',
    states: {
      __typename: 'UIStates'
    },
    values: {
      __typename: 'UIValues'
    }
  }
};

并且在您的查询中必须引用标量或数组,因此自然地您的查询将不起作用:

const QUERY = gql`
  {
    ui @client {
      states
      values
    }
  }
`;

您需要更深入地研究标量或数组:

const QUERY = gql`
  {
    ui @client {
      states {
        foo
      }
      values {
        bar
      }
    }
  }
`;

其余的取决于您的数据。

如果您需要过滤查询-您的查询将如下所示(第一个查询返回所有项目,下一个查询通过ID过滤项目)

const TEST_CLIENT_FILTER_QUERY = gql`
  {
    bzz @client {
      id
      title
    }
    bzz(id: $id) @client {
      id
      title
    }
  }
`

您也将需要一个解析器。这是很脏的,我不确定这是 apollo-graphql 的方式,但是目前为止它对我来说还是有用的。

    resolvers: {
      Query: {
        bzz: (root, variables, context) => {
          const key = context.getCacheKey({ __typename: 'bzz', id: variables.id })
          return context.cache.extract()[key]
        },
      },
    },

这些是我对此类查询的默认设置:

  cache.writeData({
    data: {
      bzz: [
        {
          __typename: 'bzz',
          id: 1,
          title: 'bzz 1',
        },
        {
          __typename: 'bzz',
          id: 2,
          title: 'bzz 2',
        },
      ],
    },
  })