apollo配置中心的组件_如何使用Apollo的全新查询组件来管理本地状态

apollo配置中心的组件

Note: This article deals with utilizing Apollo’s brand new Query and Mutation components, instead of the HOCs. For those that have read the original article here, be aware that the two articles are very similar.

注意:本文涉及利用Apollo全新的查询和突变组件,而不是HOC。 对于那些在这里阅读原始文章的人,请注意,这两篇文章非常相似。

介绍 (Introduction)

One of Web Development’s biggest strengths — and weaknesses — is its approach to modularity. A key programming mantra is to choose something (a function, a package) to do a single job and to do it well. The downside to this approach is that a single project can involve juggling dozens of separate technologies and concepts, each focusing on something specific.

Web开发的最大的优点和缺点之一是它的模块化方法。 编程的一项重要原则是选择某些东西(函数,程序包)来完成一项工作并将其做好。 这种方法的缺点是,一个项目可能涉及许多单独的技术和概念,而每个技术和概念都专注于特定的事物。

So choosing Apollo Client to handle my local state as well as my remote data seems like a no brainer. Why deal with Redux’s boilerplate and idioms when I’ve already got Apollo/GraphQL set up to get data from my backend?

因此,选择Apollo Client处理我的本地状态以及我的远程数据似乎毫无道理。 当我已经设置好Apollo / GraphQL从后端获取数据时,为什么还要处理Redux的样板和习惯用法呢?

While this article is going to deal with setting up Apollo to handle local state, it’s not going to be an introduction to the tech. (This legit howtographql tutorial is a good start for that).

尽管本文将介绍如何设置Apollo来处理本地状态,但这并不是对技术的介绍。 (本合法的howtographql教程是一个很好的开始)。

Note: The finished repo can be found here. You can pore through the code if you get stuck or feel confused.

注意:可以在此处找到完成的仓库。 如果遇到困难或感到困惑,可以仔细阅读代码。

设定 (Getting set up)

We’ll start by cloning the corresponding repo from here. This repo contains a simple react website, with a sidebar, header, and a body. It’s pretty static in nature, no dynamic content (…yet). By the end of this tutorial, we’ll have Apollo managing the state of the website. Clicking an item in the sidebar will change the state of the website, which in turn updates the header to display the new data.

我们将从这里克隆相应的仓库开始 。 此仓库包含一个简单的react网站,带有侧边栏,标题和正文。 它本质上是静态的,没有动态的内容(…至今)。 在本教程结束时,我们将让Apollo管理网站的状态。 单击侧栏中的一个项目将更改网站的状态,这反过来会更新标题以显示新数据。

If you check package.json you’ll see that we’ve only got the basics, plus some additional packages pertaining to our parcel setup.

如果您检查package.json您会发现我们只有基本知识,还有一些其他与包裹设置有关的包裹。

After cloning the repo, run your standard commands in your command line interface.

克隆存储库后,在命令行界面中运行标准命令。

> yarn
> yarn dev

To install all of your packages and to whip up a local server, go to localhost:1234 and you’ll hopefully see the demo website in all of its glory. It’s static right now, so clicking around won’t do a thing.

要安装所有软件包并启动本地服务器,请转至localhost:1234,您将有幸看到该演示网站。 现在它是静态的,因此单击鼠标无助。

What we want to do first and foremost is to get Apollo in our project, so install these packages. apollo-client lets us configure our instance of Apollo, and react-apollo is the driver that allows us to integrate it into our React application. Due to an issue with parcel (I think) we’ll also need to install graphql.

我们首先要做的是在我们的项目中使用Apollo,因此请安装这些软件包。 apollo-client让我们配置Apollo的实例,而react-apollo是使我们能够将其集成到React应用程序中的驱动程序。 由于包裹问题(我认为),我们还需要安装graphql

> yarn add apollo-client react-apollo graphql

Create a new directory src/apollo, crack open an index.js file, and add the following:

创建一个新目录src/apollo ,打开一个index.js文件,并添加以下内容:

import ApolloClient from ‘apollo-client’;
export const client = new ApolloClient({});

This initializes our Apollo Client, which we will then use to wrap our React application by adding the following inside of our src/index.js file.

这将初始化我们的Apollo客户程序,然后通过在src/index.js文件内部添加以下内容来包装React应用程序。

import { ApolloProvider } from ‘react-apollo’;
import { client } from ‘./apollo’;

const WrappedApp = (
  <ApolloProvider client={client} >
    <App />
  </ApolloProvider>
);

ReactDOM.render(WrappedApp, document.getElementById(‘root’));
// Don’t be a sap. Wrap your app.

We now have Apollo ready to use in our app. Everything builds when we restart our dev server, but we get an error when we try and access it in the browser. The console will tell us that we need to specify the link and cache properties for our Apollo client, so let’s do that.

现在,我们已经准备好在应用程序中使用Apollo。 重新启动开发服务器时,所有内容都会生成,但尝试在浏览器中访问它时会出现错误。 控制台将告诉我们,我们需要为Apollo客户端指定链接和缓存属性,所以让我们开始吧。

> yarn add apollo-link apollo-cache-inmemory apollo-link-state

The previous line adds the new Apollo dependencies to our application while the following code resolves the console errors we were getting. So go back to apollo/index.js and update it so the file looks like this:

前一行将新的Apollo依赖项添加到我们的应用程序中,而以下代码解决了我们遇到的控制台错误。 因此,请回到apollo/index.js并对其进行更新,以使该文件如下所示:

import ApolloClient from ‘apollo-client’;
import { InMemoryCache } from ‘apollo-cache-inmemory’;
import { ApolloLink } from ‘apollo-link’;
import { withClientState } from ‘apollo-link-state’;

const cache = new InMemoryCache();
const stateLink = withClientState({
  cache
});

export const client = new ApolloClient({
  cache,
  link: ApolloLink.from([
    stateLink,
  ]),
})

Let’s create an instance of our cache. The cache is Apollo’s normalized data store that stores the results of the query in a flattened data structure. We will read from the cache when we make our GraphQL query, and we’ll write to the cache when we make our mutation resolver.

让我们创建一个缓存实例。 缓存是Apollo的规范化数据存储,用于将查询结果存储在扁平化的数据结构中。 进行GraphQL查询时,我们将从缓存中读取数据,而在进行突变解析器时,我们将写入缓存中。

You can see we’ve also added link to our client object. The ApolloLink.from()method lets us modularly configure how our queries are sent over HTTP. We can use this to handle errors and authorization, and to provide access to our backend. We’re not going to be doing any of this in the tutorial, but we will set up our client state here. So we create const stateLink above and pass in our cache. We’ll add our default state and resolvers here later.

您可以看到我们还向我们的客户端对象添加了linkApolloLink.from()方法使我们能够模块化配置如何通过HTTP发送查询。 我们可以使用它来处理错误和授权,并提供对后端的访问。 在本教程中,我们不会做任何事情,但是我们将在这里设置客户端状态。 因此,我们在上面创建const stateLink并传递我们的缓存。 我们稍后将在此处添加默认状态和解析器。

Going back to the browser, you’ll see our lovely static site displaying in all of its magnificence. Let’s add some default state to our project and fire off our first query.

回到浏览器,您会看到我们可爱的静态站点以其所有的宏伟功能显示。 让我们向项目中添加一些默认状态,并触发第一个查询。

Inside of the Apollo directory, create a new directory called defaults and add an index.js inside of it. The file will contain the following:

在Apollo目录内,创建一个名为defaults的新目录,并在其中添加index.js 。 该文件将包含以下内容:

export default {
  apolloClientDemo: {
    __typename: ‘ApolloClientDemo’,
    currentPageName: ‘Apollo Demo’,
  }
}

We create an object which acts as the default state of our site. apolloClientDemo is the name of the data structure we want to access when we make our queries. The __typename is the mandatory identifier that our cache uses, and the currentPageName is the specific item of data that our header will use to — you guessed it — display the current page name.

我们创建一个充当站点默认状态的对象。 apolloClientDemo是我们进行查询时要访问的数据结构的名称。 __typename是我们的缓存使用的强制性标识符,而currentPageName是我们的标头将用于显示您当前页面名称的特定数据项。

We’ll need to add this to our apollo/index.js file:

我们需要将其添加到我们的apollo/index.js文件中:

import defaults from ‘./defaults’;

const stateLink = withClientState({
  cache,
  defaults,
});

Let’s clear this up a little bit. import and default are both keywords associated with importing modules, but coincidentally the name of the object we’re exporting from ./defaults is also called defaults (so don’t be thinking that I’m using import/export wrong). Treat this import line as if it was just any regular ol’ named import.

让我们澄清一下。 importdefault都是与导入模块相关联的关键字,但是巧合的是,我们从./defaults导出的对象的名称也称为defaults (所以不要以为我使用的import/export错误)。 将此导入行视为仅仅是任何常规的名为import的行。

With that out of the way, let’s go make a query!

有了它,让我们开始查询!

如何进行查询 (How to make a query)

Add the following package to your project:

将以下包添加到您的项目:

> yarn add graphql-tag

and create a new directory src/graphql. In there, create two new files: index.js and getPageName.js. The GraphQL directory will house all the queries and mutations. We’ll create our query in getPageName.js by writing the following:

并创建一个新目录src/graphql 。 在其中创建两个新文件: index.jsgetPageName.js 。 GraphQL目录将容纳所有查询和变异。 我们将通过编写以下代码在getPageName.js创建查询:

import gql from ‘graphql-tag’;

export const getPageNameQuery = gql`
  query {
    apolloClientDemo @client {
      currentPageName
    }
  }
`;

export const getPageNameOptions = ({
  props: ({ data: { apolloClientDemo } }) => ({
    apolloClientDemo
  })
});

So we’re exporting two variables, the query and the options. If you’ve used GraphQL before, then the query will look familiar. We’re querying against the apolloClientDemo data structure, retrieving back nothing more than the currentPageName. You’ll notice that we’ve added the @client directive to our query. This tells Apollo to query our local state instead of sending the request to the backend.

因此,我们导出了两个变量,即查询和选项。 如果您以前使用过GraphQL,则查询看起来很熟悉。 我们正在查询apolloClientDemo数据结构,只检索了currentPageName。 您会注意到,我们已经在查询中添加了@client指令。 这告诉Apollo查询我们的本地状态,而不是将请求发送到后端。

Below you’ll see that we’re exporting some options. This is simply defining how we want the data to look when we map the results to the props. We’re destructuring the GraphQL response and sending it to our view so it looks like this:

在下面,您将看到我们正在导出一些选项。 这只是在定义将结果映射到道具时我们希望数据如何显示。 我们正在分解GraphQL响应并将其发送到我们的视图,因此它看起来像这样:

props: {
  currentPageName: ‘Apollo Demo’,
}
// and not this
props: {
  data: {
    apolloClientDemo: {
      currentPageName: ‘Apollo Demo’,
    }
  }
}

Go to the graphql/index.js file and export the query as follows:

转到graphql/index.js文件,并按如下所示导出查询:

export { getPageNameQuery, getPageNameOptions } from ‘./getPageName’;

Again, while this isn’t completely necessary for a small demo/project, this file is handy should your application grow larger. Having your queries exported from a single centralized location keeps everything organized and scalable.

同样,尽管对于一个小型演示/项目而言,这并不是完全必要的,但如果您的应用程序变大,此文件将很方便。 从单个集中位置导出查询可以使所有内容井井有条且可扩展。

Add to your Header.js:

添加到您的Header.js:

import React from 'react';
import { Query } from 'react-apollo';
import { getPageNameQuery } from '../graphql';

const Header = () => (
    <Query query={getPageNameQuery}>
        {({ loading, error, data }) => {
            if (error) return <h1>Error...</h1>;
            if (loading || !data) return <h1>Loading...</h1>;

            return <h1>{data.apolloClientDemo.currentPageName}</h1>
        }}
    </Query>
);

export default Header;

This is our first use of Apollo’s new Query Component, which was added in 2.1. We import Query from react-apollo and use it to wrap the rest of our component. We then pass the getPageNameQuery as a value in the query prop. When our component renders, it fires off the query and gives the rest of the component access to the data, which we destructure to gain access to loading, errors, and data.

这是我们在2.1中添加的Apollo新查询组件的首次使用。 我们从react-apollo导入Query ,并使用它包装其余组件。 然后,我们将getPageNameQuery作为值传递给查询道具。 当我们的组件渲染时,它会触发查询并为其余组件提供对数据的访问权限,我们对其进行了结构分解以获得对加载,错误和数据的访问权限。

The Query Component uses the render props pattern to give the rest of our component access to the information returned from the query. If you’ve used the React Context API in 16.3, then you’ve seen this syntax before. Otherwise it’s worth checking out the official React docs here, as the Render Props pattern is becoming increasingly popular.

查询组件使用render props模式来使其余组件可以访问查询返回的信息。 如果您在16.3中使用过React Context API,那么您之前已经看过这种语法。 否则,值得在这里查看官方的React文档,因为Render Props模式变得越来越流行。

In our component, we do a few checks to see if there were any errors when firing the query or if we’re still waiting for data to be returned. If either of these scenarios are true, we return the corresponding HTML. If the query was fired correctly, the component will dynamically display the title of the current page. As we haven’t added our mutation yet, it will only display the default value. But you can change whatever’s in the default state and the website will reflect that.

在我们的组件中,我们进行了一些检查,以查看在触发查询时是否存在任何错误,或者是否仍在等待返回数据。 如果以上两种情况都成立,则返回相应HTML。 如果查询正确触发,则组件将动态显示当前页面的标题。 由于我们尚未添加突变,因此它将仅显示默认值。 但是您可以更改默认状态下的任何内容,网站将对此进行反映。

Now all that’s left to do is mutate the data in the Apollo cache by clicking on the sidebar item.

现在剩下要做的就是通过单击侧边栏项目来更改Apollo缓存中的数据。

变异 (Mutations)

Things get a little more complicated when dealing with mutations. We no longer just retrieve data from the Apollo store, but we update it too. The architecture of mutation is as follows:

处理突变时,事情变得更加复杂。 我们不再只是从Apollo存储中检索数据,而是也会对其进行更新。 突变的架构如下:

> User clicks sidebar item

>用户点击侧栏项目

> Sends variable to mutation

>变量可变

> Fires mutation with variable

>具有可变性的Fires突变

> Gets sent to the instance of Apollo

>已发送给阿波罗实例的礼物

> Finds corresponding resolver

>查找对应的解析器

> Applies logic to the Apollo store

>将逻辑应用于阿波罗商店

> Sends data back to header

>将数据返回标题

If that’s difficult to remember, then use this handy mnemonic created using a mnemonic generator: Urban Senile Fauns Groped Faithless Aslan Solemnly. (easy…)

如果这很难记住,请使用由助记符生成器创建的便捷助记符:Urban Senile Fauns毫无信仰地摸索了Aslan。 (简单…)

Start by creating a file graphql/updatePageName.js.

首先创建一个文件graphql/updatePageName.js

import gql from ‘graphql-tag’;

export const updatePageName = gql`
  mutation updatePageName($name: String!) {
    updatePageName(name: $name) @client {
      currentPageName
    }
  }
`;

and export it just like we did with the query.

然后将其导出,就像我们对查询所做的一样。

export { updatePageNameMutation } from ‘./updatePageName’;

You’ll notice a few differences regarding the mutation. First off we’ve changed the keyword from query to mutation. This lets GraphQL know the type of action we’re performing. We’re also defining the name of the query and adding types to the variables we’re passing in. Inside here we’re specifying the name of the resolver we’ll be using to carry out the changes. We’re also passing through the variable and adding the @client directive.

您会注意到有关突变的一些差异。 首先,我们将关键字从查询更改为突变。 这使GraphQL知道我们正在执行的操作的类型。 我们还定义了查询的名称,并将类型添加到所传递的变量中。在此内部,我们指定了将用于执行更改的解析器的名称。 我们还将通过变量并添加@client指令。

Unlike the query, we can’t just add the mutation to our view and expect anything to happen. We’ll have to go back to our Apollo directory and add our resolvers. So go ahead and create a new directory apollo/resolvers, and files index.js and updatePageName.js. Inside of updatePageName.jsadd the following:

与查询不同,我们不能只是将变异添加到视图中并期望发生任何事情。 我们必须返回到Apollo目录并添加解析器。 因此,继续创建一个新目录apollo/resolvers ,以及文件index.jsupdatePageName.js 。 在updatePageName.js添加以下内容:

import gql from ‘graphql-tag’;

export default (_, { name }, { cache }) => {
  const query = gql`
    query GetPageName {
      apolloClientDemo @client {
        currentPageName
      }
    }
  `;
  
  const previousState = cache.readQuery({ query });
  
  const data = {
    apolloClientDemo: {
      …previousState.apolloClientDemo,
      currentPageName: name,
    },
  };
  
  cache.writeQuery({
    query,
    data,
  });
  
  return null;
};

There are a lot of interesting things going on in this file. Fortunately, it’s all very logical and doesn’t add many new concepts to what we’ve seen before.

该文件中发生了很多有趣的事情。 幸运的是,这都是非常合乎逻辑的,不会在我们之前看到的内容中添加很多新概念。

So by default, when a resolver gets called, Apollo passes in all of the variables and the cache. The first argument is a simple ‘_’ because we don’t need to use it. The second argument is the variables object, and the final argument is the cache.

因此,默认情况下,当调用解析器时,Apollo会传入所有变量和缓存。 第一个参数是一个简单的“ _”,因为我们不需要使用它。 第二个参数是变量对象,最后一个参数是缓存。

Before we can make changes to the Apollo store, we’ll need to retrieve it. So we make a simple request to get the current content from the store and assign it to previousState. Inside of the data variable, we create a new object with the new information we want to add to the store, which we then write to. You can see that we’ve spread the previous state inside of this object. This is so that only the data we explicitly want to change gets updated. Everything else remains as it is. This prevents Apollo from needlessly updating components whose data hasn’t changed.

在对Apollo商店进行更改之前,我们需要对其进行检索。 因此,我们提出了一个简单的请求,以从商店中获取当前内容并将其分配给previousState。 在数据变量内部,我们创建了一个新对象,并带有要添加到存储中的新信息,然后将其写入其中。 您可以看到我们已经在该对象内部传播了先前的状态。 这样一来,只有我们明确想要更改的数据才会更新。 其他一切都保持原样。 这样可以防止Apollo不必要地更新其数据未更改的组件。

Note: while this isn’t completely necessary for this example, it’s super useful when queries and mutations handle larger amounts of data, so I’ve kept it in for the sake of scalability.

注意:虽然对于本示例而言,这并不是完全必要的,但是当查询和变异处理大量数据时,它非常有用,因此为了可扩展性,我将其保留在其中。

Meanwhile in the resolvers/index.js file…

同时,在resolvers/index.js文件中…

import updatePageName from ‘updatePageName’;

export default {
  Mutation: {
    updatePageName,
  }
};

This is the shape of object that Apollo expects when we pass in our resolvers in to stateLink back in apollo/index.js:

这是当我们将解析器传入apollo/index.js stateLink时,Apollo期望的对象形状:

import resolvers from ‘./resolvers’;

const stateLink from = withClientState({
  cache,
  defaults,
  resolvers,
});

All that’s left to do is add the mutation to our sidebar component.

剩下要做的就是将变异添加到我们的侧边栏组件中。

// previous imports
import { Mutation } from ‘react-apollo’;
import { updatePageNameMutation } from ‘../graphql’;

class Sidebar extends React.Component {
  render() {
    return (
      <Mutation mutation={updatePageNameMutation}>
        {updatePageName => (
          // outer div elements
          <li className=“sidebar-item” onClick={() => updatePageName({ variables: { name: ‘React’} })}>React</li>
          // other list items and outer div elements
        )}
      </Mutation>
    );
  }
}

export default Sidebar;

Like our resolver file, there’s a lot going on in this file — but it’s new. We import our Mutation component from react-apollo, wrap it around our component, and pass the updatePageNameMutation inside of the mutation prop.

像我们的解析器文件一样,此文件中有很多事情在进行-但它是新的。 我们从react-apollo导入我们的Mutation组件,将其包裹在我们的组件周围,并在mutation prop内部传递updatePageNameMutation

The component now has access to the updatePageName method which fires the mutation whenever it’s called. We do this by adding the method as a handler to the <li>’s onClick property. The method expects to receive on object containing the variables as a parameter, so pass in the name you want to update the header to. If everything works, you should be able to run your dev server and click the sidebar items, which should then change our header.

现在,该组件可以访问updatePageName方法,该方法将在每次调用该突变时触发该突变。 为此,我们将方法作为处理程序添加到< li>的onClick属性中。 该方法希望接收包含变量作为参数的对象,因此请传入您想要将标头更新为的名称。 如果一切正常,您应该能够运行开发服务器并单击侧栏项目,然后应更改我们的标题。

结语 (Wrapping up)

Hooray! Hopefully everything worked out. If you got stuck, then check out the repo here. It contains all of the finished code. If you’re thinking of using local state management in your next React app, then you can fork this repo and continue from there. If you’re interested in having this article/topic spoken about at a meetup or conference, then send a message my way!

万岁! 希望一切顺利。 如果您遇到问题,请在此处查看回购。 它包含所有完成的代码。 如果您正在考虑在下一个React应用程序中使用本地状态管理,则可以派生此存储库并从那里继续。 如果您有兴趣在聚会或会议上谈论这篇文章/主题,请以我的方式发送消息!

There’s a lot more I wanted to cover in this tutorial, such as async resolvers (think Redux thunk), type checking/creating a schema, and a mutation update. So who knows… maybe I’ll drop another article sometime soon.

在本教程中,我想讲的内容更多,例如异步解析器(认为Redux很重要),类型检查/创建模式以及突变更新。 所以谁知道呢……也许我会很快再发表另一篇文章。

I really hope that this tutorial was useful for you. I’d like to shout out Sara Vieira’s youtube tutorial too, as it helped me get my head around Apollo Client. If I haven’t done my job well enough by leaving you scratching your head, then follow the link. And finally, feel free to hit me up on social media, I’m a big music and tech fan so talk geek to me.

我真的希望本教程对您有用。 我也想大声疾呼Sara Vieira的youtube教程 ,因为它帮助我了解了Apollo Client。 如果我抓不住头做得还不够好,请点击链接。 最后,您可以随时在社交媒体上打我,我是音乐和科技的忠实粉丝,所以请跟我说怪胎。

谢谢阅读! (Thanks for reading!)

If you’re interested in hosting me at a conference, meetup or as a speaking guest for any engagement then you can DM me on twitter!

如果您有兴趣主持我的会议,聚会或作为演讲嘉宾参加任何活动,则可以在twitter发给我DM!

How to use Apollo’s brand new Query components to manage local state

如何使用Apollo的全新查询组件来管理本地状态

Add a touch of Suspense to your web app with React.lazy()

使用React.lazy()向您的Web应用添加一丝悬念

No need to wait for the holidays, start Decorating now

无需等待假期,立即开始装修

Managing local state with Apollo and Higher Order Components

使用Apollo和高阶组件管理本地状态

The React Conference drinking game

React Conference饮酒游戏

Develop and Deploy your own React monorepo app in under 2 hours, using Lerna, Travis and Now

使用Lerna,Travis和Now在2小时内开发和部署自己的React monorepo应用

翻译自: https://www.freecodecamp.org/news/updated-for-apollo-v2-1-managing-local-state-with-apollo-d1882f2fbb7/

apollo配置中心的组件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值