{"id":22923279,"url":"https://github.com/khriztianmoreno/apollo-client-workshop-react","last_synced_at":"2025-05-12T23:13:50.663Z","repository":{"id":36274287,"uuid":"173477429","full_name":"khriztianmoreno/apollo-client-workshop-react","owner":"khriztianmoreno","description":"Workshop Introduction: GraphQL with React and Apollo Client","archived":false,"fork":false,"pushed_at":"2023-03-04T03:15:24.000Z","size":1073,"stargazers_count":32,"open_issues_count":7,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-01T04:32:46.328Z","etag":null,"topics":["apollo-client","graphql","hacktoberfest","javascript","react","reactjs","workshop","workshop-materials"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/khriztianmoreno.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-02T17:25:19.000Z","updated_at":"2025-03-25T04:20:29.000Z","dependencies_parsed_at":"2023-01-17T00:02:17.622Z","dependency_job_id":null,"html_url":"https://github.com/khriztianmoreno/apollo-client-workshop-react","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khriztianmoreno%2Fapollo-client-workshop-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khriztianmoreno%2Fapollo-client-workshop-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khriztianmoreno%2Fapollo-client-workshop-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khriztianmoreno%2Fapollo-client-workshop-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khriztianmoreno","download_url":"https://codeload.github.com/khriztianmoreno/apollo-client-workshop-react/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253837468,"owners_count":21971984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollo-client","graphql","hacktoberfest","javascript","react","reactjs","workshop","workshop-materials"],"created_at":"2024-12-14T08:14:55.281Z","updated_at":"2025-05-12T23:13:50.640Z","avatar_url":"https://github.com/khriztianmoreno.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GraphQL Apollo-Client Workshop\n\nGraphQL es una de las tecnologías mas interesantes hoy en día por una buena razón. Permite a un cliente realizar consultas específicas, complejas y/o agregar consultas de datos, además es fácil de iniciar.\n\nSi has querido aprender GraphQL y comenzar a aprovechar sus poderes, este es un excelente lugar para comenzar.\n\nEste taller es para principiantes de GraphQL con una comprensión básica de React. Se enfoca en la implementación del lado del cliente del uso de GraphQL usando el cliente Apollo.\n\n¡Bienvenido! Hoy aprenderemos cómo construir una aplicación React para consumir datos desde GraphQL utilizando y Apollo Client. ¡Empecemos! 🚀\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"GraphQL Data in React with Apollo Client\" src=\"./.readme-static/apollo-graphql-react.png\" width=\"400\" /\u003e\n\u003c/p\u003e\n\n## Overview\n\nEste taller le proporcionará una introducción breve pero completa sobre cómo recuperar y actualizar datos remotos desde un *endpoint* GraphQL, así como la administración del estado local utilizando [Apollo Client](https://github.com/apollographql/apollo-client) en combinación con React. La experiencia con React no es obligatoria, pero lo más probable es que sea de mucha ayuda al ver este taller.\n\nSi bien este curso se dirige claramente a los principiantes de Apollo y GraphQL, incluso alguien con bastante experiencia en Apollo podría aprender algo nuevo en la lección sobre cómo administrar el estado local.\n\nCada lección se centra en una característica o concepto específico y se puede ver de forma independiente. Aún así, todo el taller está cuidadosamente diseñado, por lo que todas las lecciones se combinan para crear una aplicación de libro de cocina. Comienza con la consulta de datos desde un *endpoint* GraphQL.\n\nDespués de eso, mejoramos la consulta utilizando variables GraphQL para filtrar resultados. Luego pasamos a las mutaciones, lo que nos permite actualizar los datos. Con las mutaciones, hay bastantes problemas a tener en cuenta.\n\nUsando el estado de enlace de Apollo, incluso podemos extender el esquema remoto GraphQL con capacidades locales. Por último, pero no menos importante, exploramos un par de utilidades bastante útiles que se envían con Apollo, como la *refetching* y *polling*.\n\nEspero que este taller te sea de utilidad y que lo disfrutes. 🙌🏻❤️\n\n## Slides\n\nSi necesita hacer referencia a las diapositivas, puede verlas[aquí](https://slides.com/khriztianmoreno/graphql-apollo-client-workshop).\n\n## Setup instructions\nComenzaremos conociendo la estructura de nuestro repositorio lo cual nos va a facilitar el trabajo, luego con una guía sobre cómo agregar el cliente Apollo a un proyecto existente, luego seguiremos usando los componentes de Consulta y Mutación para obtener y manipular datos usando una API GraphQL.\n\n### Prerequisites\n\n- [Node.js LTS](https://nodejs.org/en/)\n- [Git](https://git-scm.com/)\n- [GitHub](https://github.com/khriztianmoreno)\n- [VSCode](https://code.visualstudio.com/)\n\nTambién necesitarás instalar [Apollo DevTools for Chrome](https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm). Si tienes todo esto, ¡entonces estamos listos para comenzar!\n\n**Estructura del proyecto**\n```\napollo-client-workshop-react\n├── server\n│    ├── models\n│    ├── resolvers\n│    ├── utils\n│    ├── package.json\n│    └── index.js\n├── client\n│    ├── // En este espacio contruiremos nuestro cliente\n└── .nvmrc\n└── LICENSE\n└── README.md\n```\n\n### Running server\n\nNuestro sevidor **GraphQL** necesita precargar unos datos de ejemplo previamente para su funcionamiento\n\n```bash\n# Move to server folder\n$ cd server\n\n# Install dependencies\n$ npm install\n\n# Load data\n$ npm run seed\n\n# Run server\n$ npm run start:slow\n```\n\n*Nota: La [base de datos](https://github.com/louischatriot/nedb) almacena dos archivos JSON almacenados en `/tmp/recipes.json` y `/tmp/ingedients.json`.*\n\n## Branches\n\n- [`00-start`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/00-start) Tu punto de partida\n- [`01-setup`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/01-setup) Configuración de Apollo Boost y React Apoll\n- [`02-query`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/02-query) Escribiendo componentes de consulta\n- [`03-dynamic-queries`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/03-dynamic-queries) Asignado variables a nuestra consulta\n- [`04-mutations`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/04-mutations-form) Escribiendo componentes de mutación\n- [`05-schema-extending`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/05-schema-extending) Modificando el schema en el cliente\n- [`06-refetch`](https://github.com/khriztianmoreno/apollo-client-workshop-react/tree/06-refetch) Obtener datos manualmente en intervalos\n\n\n## Client's Installation\nPrimero iniciemos un proyecto React usando npx y Create React App:\n\n```bash\n$ npx create-react-app client\n```\n\nA continuación, puede grabar en el proyecto e iniciar un servidor de desarrollo local:\n\n```bash\n$ cd client \u0026\u0026 npm start\n```\n\n## Setup and Connect an Apollo Client to a React Application with Apollo Provider\nAprenderemos cómo configurar el cliente Apollo utilizando [Apollo Boost](https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost), conectarlo a un *endpoint* GraphQL y usar el proveedor Apollo para conectarlo a nuestra aplicación React. Además demostramos cómo usar un **Apollo Consumer**.\n\nLuego de tener nuestra aplicación React creada inicialmente usando `create-react-app`. Vamos a comenzar con Apollo Client, vamos a instarlar las dependencias necesarias para agregar los siguientes paquetes npm, `GraphQL`, `apollo-boost` y `react-apollo.`\n\n**Debemos estar dentro de la carpeta `client`**\n\n```bash\n# Install dependencies\n$ npm i -S graphql apollo-boost react-apollo\n```\nEl paquete GraphQL es necesario para ciertas características, como el análisis de consultas GraphQL. `Apollo-boost` es un paquete que viene con el `ApolloClient` bien configurado para comenzar rápidamente. Por último, pero no menos importante `react-apollo`, integra Apollo con React proporcionando múltiples componentes y utilidades.\n\nSimplifiqué el por defecto del archivo `/client/src/App.js`.\n```jsx\nclass App extends Component {\n  render() {\n    return (\n      \u003cdiv className=\"App\"\u003e\n        \u003cheader className=\"App-header\"\u003e\n          \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\n        \u003c/header\u003e\n\n        \u003cdiv\u003eHello World!\u003c/div\u003e\n\n      \u003c/div\u003e\n    );\n  }\n}\n\nexport default App;\n```\n\nLuego, importamos ApolloClient desde `apollo-boost` e instanciamos un nuevo `ApolloClient()`. La única opción obligatoria que debemos proporcionar es **uri**: para nuestro *endpoint* GraphQL. En este caso, estamos usando \"http://localhost:4000/\", ya que allí ya tengo un servidor GraphQL en ejecución.\n\n```js\nimport ApolloClient from \"apollo-boost\";\n\nconst client = new ApolloClient({\n  uri: \"http://localhost:4000/\"\n});\n```\n\nVamos a verificar que nuestro cliente funciona como se espera al solicitar datos de nuestro *endpoint* GraphQL mediante una consulta. Nuestro `client` espera un objeto que contiene al menos la propiedad de `query`.\n\nEscribimos uno usando la notación de *template tag*. ¿Qué busca ahora? Nuestro backend es un libro de cocina que contiene recetas. Para empezar, podemos crear todas las recetas. Para cada uno de ellos, solicitamos el `id` y el `title`.\n\n```js\nclient\n  .query({\n    query: gql`\n      {\n        recipes {\n          id\n          title\n        }\n      }\n    `\n  })\n```\n\nSi observas, puedes ver que es necesario importar `gql`, esta dependencia es un *template literal string* que analiza las consultas de GraphQL en el estandar *AST*. Las cadenas GraphQL son la forma correcta de escribir consultas en nuestro código, ya que pueden analizarse de forma estática utilizando herramientas como [eslint-plugin-graphql](https://github.com/apollostack/eslint-plugin-graphql). Sin embargo, las cadenas son un inconveniente de manipular, si está tratando de hacer cosas como agregar campos adicionales, combinar varias consultas entre sí u otras cosas interesantes.\n\nAhí es donde entra en juego este paquete: le permite escribir sus consultas con los [literales de plantilla de ES2015](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) y compilarlas en un AST con la etiqueta `gql`.\n\nPara utilizarlo solo es necesario agregar la siguiente linea en el top de nuestro archivo:\n\n```js\nimport gql from \"graphql-tag\";\n```\n\nVolviendo a nuestro código, una vez que la consulta se resuelve, imprimimos los resultados.\n\n```js\nclient\n  .query({\n    query: gql`\n      {\n        recipes {\n          id\n          title\n        }\n      }\n    `\n  })\n.then(result =\u003e console.log(result));\n```\n\nComo puede ver, una vez que cargamos la página, la consulta se ejecutó y nuestro resultado se registró en la consola. Hasta ahora vamos bien.\n\n![Console log](./.readme-static/01.png)\n\nYa que tenemos `ReactApollo` disponible, veamos cómo podemos configurarlo y usarlo dentro de nuestra función de render. `import { ApolloProvider } from \"react-apollo\",`. El `ApolloProvider` requiere una instancia de Apollo `{cliente}` En nuestro caso, tomamos el que ya inicializamos y una vez configurado el `ApolloProvider` ahora pasamos el cliente por el árbol de *rendering* a través de una función de *React context*.\n\n```jsx\nimport { ApolloProvider } from \"react-apollo\";\n\nclass App extends Component {\n  render() {\n    return (\n      \u003cApolloProvider client={client}\u003e\n        \u003cdiv className=\"App\"\u003e\n          \u003cheader className=\"App-header\"\u003e\n            \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\n          \u003c/header\u003e\n          \u003cdiv\u003eHello World!\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/ApolloProvider\u003e\n    );\n  }\n}\n```\n\nAhora vamos a usar un `ApolloConsumer`, donde podemos aprovechar esta configuración para usar nuestro cliente para hacer consultas más a fondo en nuestro árbol de renderizado de React.\n\n```js\nimport { ApolloProvider, ApolloConsumer } from \"react-apollo\";\n```\n\nEn este caso, tomamos nuestra consulta existente y la ejecutamos dentro de una *render prop* de `ApolloConsumer`.\n\n```jsx\nclass App extends Component {\n  render() {\n    return (\n      \u003cApolloProvider client={client}\u003e\n        \u003cdiv\u003eHello World!\u003c/div\u003e\n        \u003cApolloConsumer\u003e\n          {client =\u003e {\n            client\n            .query({\n              query: gql`\n                {\n                  recipes {\n                    id\n                   title\n                  }\n                }\n              `\n            })\n            .then(result =\u003e console.log(result));\n\n             return null;\n          }}\n        \u003c/ApolloConsumer\u003e\n      \u003c/ApolloProvider\u003e\n    );\n  }\n}\n```\n\n*Para cumplir con las expectativas de la API de React, devolvimos null.*\n\nSi bien `ApolloConsumer` puede ser útil en algunos casos, la mayoría de las veces, utilizará el componente de Consulta/Mutación o componentes de orden superior, todos en ReactApollo.\n\n## Fetch Data using the Apollo Query Component\n\nPara mostrar los datos, primero tenemos que buscarlos. El componente `Query` nos permite describir qué datos nos interesan y manejar automáticamente la obtención de nuestros datos. Una vez que recibimos los datos podemos procesarlos usando React. Dado que el componente `Query` maneja la obtención de datos, debemos asegurarnos de que tratamos adecuadamente los casos de un estado de carga, así como el momento en que se reciben los errores de la API de GraphQL. En esta lección cubriremos ambos.\n\nVamos a comenzar con cambiar nuestro `ApolloConsumer` por el componente `Query`. El componente de *Query* nos permite obtener datos y proporciona los datos como una *render prop*. El componente de consulta tiene un **prop obligatorio**, `query`. Para la consulta, volveremos a utilizar la etiqueta gql con una cadena de consulta dentro de ella. Finalmente tendremos nuestro código de esta forma:\n\n```jsx\nimport React, { Component } from \"react\";\nimport ApolloClient from \"apollo-boost\";\nimport gql from \"graphql-tag\";\nimport { ApolloProvider, Query } from \"react-apollo\";\n\nconst client = new ApolloClient({\n  uri: \"http://localhost:4000/\"\n});\n\nclass App extends Component {\n  render() {\n    return (\n      \u003cApolloProvider client={client}\u003e\n        \u003cQuery\n          query={gql`\n            {\n              recipes {\n                id\n                title\n              }\n            }\n          `}\n          /\u003e\n      \u003c/ApolloProvider\u003e\n    );\n  }\n}\n\nexport default App;\n```\n\nEl componente `Query` utiliza el patrón de [`render props`](https://reactjs.org/docs/render-props.html) para devolvernos los datos de la consulta. De acuerdo a este patrón el *component child* de `query` debe ser precisamente una función.\n\n```jsx\n{\n  ({ data }) =\u003e {\n    if (data.recipes === undefined) return null;\n\n    return (\n      \u003cul\u003e\n        {data.recipes.map(({ id, title }) =\u003e\n          \u003cli key={id}\u003e{title}\u003c/li\u003e\n        )}\n      \u003c/ul\u003e\n    )\n  }\n}\n```\n\nDesglosemos las cosas importantes que suceden aquí:\n\nEl componente `Query` de Apollo toma un prop *query* requerida con una consulta GraphQL que ha sido analizada usando el `gql` de `graphql-tag`. `Query` también toma una prop requerida `children` que debería ser una función. La función recibe una propiedad a la cual nosotros le hacemos *destructoring* para obtener solo la propiedad `data`, este objeto `data` inicialmente está vacío, pero una vez finalizada la carga, contiene los resultados de nuestra consulta. Validamos que la data tenga `recipes` adjuntas para mostralos en pantalla, de lo contrario `return null`.\n\n![Fetch data](./.readme-static/02.png)\n\nFunciona de maravilla.\n\nEn este momento, no sabemos por qué nuestras `recipes` no están definidas, simplemente si no estan no las mostramos en pantalla, pero es probable que puedan estar cargandose o que ocurrió un error. Para ayudar con esto, el componente de *query* expone dos propiedades más, `loading` y `error`. Las cuales nos permiten ejecutar una UI diferente según el estado de la consulta.\n\n```jsx\n{\n  ({ data,loading, error }) =\u003e {\n    if (loading) return \u003cp\u003eLoading...\u003c/p\u003e;\n    if (error) return \u003cp\u003eSomething went wrong\u003c/p\u003e;\n\n    return (\n      \u003cul\u003e\n        {data.recipes.map(({ id, title }) =\u003e\n          \u003cli key={id}\u003e{title}\u003c/li\u003e\n        )}\n      \u003c/ul\u003e\n    )\n  }\n}\n```\n\nSi actualizamos la aplicación web, podemos ver nuestra UI de *loading* hasta que finalice la carga y lleguen los datos reales.\nPara probar el error de la interfaz de usuario, detenemos el servidor. Como era de esperar, ahora vemos que el caso de error se muestra.\n\n### TODO\nAntes de terminar esta lección sobre el componente de consulta, queremos refactorizar el código un poco.\n\n- [-] Extrar el componente de consulta, crear un `recipes.js` .\n- [-] Crear una variable `QUERY` para tener nuestro `gql` query.\n- [-] Agregar importaciones necesarias.\n- [-] Actualizar `App.js` para funcionar con nuestro refactor.\n\n## Provide Dynamic Arguments in a Apollo Query Component with GraphQL Variables\n\nGraphQL soporta parámetros para consultas vía variables. Nos permiten aportar argumentos dinámicos. A menudo queremos que estas variables dependan de las decisiones tomadas por un usuario. En esta sesión, veremos cómo implementar una variable de filtro basada en un elemento de checkbox en la UI utilizando el componente de consulta de Apollo.\n\nPara filtrar recetas vegetarianas, nuestra receta acepta un argumento booleano, `vegetarian`.\n\n```js\nconst QUERY = gql`\n{\n  recipes(vegetarian: true)  {\n    id\n    title\n  }\n}\n`\n```\n\nUna vez que la página se vuelve a cargar, podemos ver que ahora solo aparecen recetas vegetarianas.\n\nEn esta lección, queremos que el argumento vegetariano dependa de una casilla de verificación, que puede ser controlada por un usuario.\n\nAntes de hacerlo, comencemos con algunos conceptos básicos. Usando la etiqueta de la plantilla `gql`, podemos asegurarnos de que un `query` debe proporcionarse con ciertas variables.\n\nPrimero declaramos un nombre para una consulta específica, usando la sintaxi de `query` y luego el nombre. Entonces indicamos que la variable `vegetarian` es de tipo `Boolean!`. Al agregar un signo de exclamación (`!`), declaramos esta variable como obligatoria.\n\n```js\nconst QUERY = gql`\n  query recipes($vegetarian: Boolean! {\n    recipes(vegetarian: $ vegetarian) {\n      id\n      title\n    }\n  }\n`;\n```\n\nPara proveer variables al componente `Query`, simplemente agregamos otra prop, `variables`. Esta prop acepta un objeto con las variables de consulta como propiedades clave-valor. En nuestro caso, nos propusimos `vegetarian: true`.\n\n```jsx\nconst Recipes = () =\u003e (\n  \u003cQuery query={QUERY} variables={{ vegetarian: true}}\u003e\n    ...\n  \u003c/Query\u003e\n)\n```\n\nAhora, actualizamos el navegador y verificamos que solo vemos comidas vegetarianas.\n\n### TODO\n\nA continuación, queremos implementar la casilla de verificación de la interfaz de usuario y hacer un seguimiento de su estado.\n\n- [-] Crear un checkbox con el label *vegetarian*.\n- [-] Capturar el evento `onChange` para este field.\n- [-] Actualizar el estado con el valor del *checkbox*.\n- [-] Asignar el estado a la variable `vegetarian` del componente `Query`.\n- [-] **BONUS!!** Usar [React Hooks](https://reactjs.org/docs/hooks-reference.html).\n\n## Update Data using the Apollo Mutation component\nEn esta lección, usaremos el componente Mutación para invocar una mutación de una API GraphQL. Además, cubrimos cómo actualizar la memoria caché local mediante consultas de recuperación, así como mejorar la experiencia del usuario cuando se trata de mutaciones. Al final, discutimos varios errores relacionados con las mutaciones y las posibles formas de abordarlos.\n\nPara esta lección, vamos a comenzar con las tareas que tú debes terminar para poder avanzar. La idea es crear un un componente `A\u001dddRecipe` que contenga dos campos de entrada, uno para el título y otro para indicar si la receta es una receta vegetariana.\n\n![Formulario para agregar recetas](./.readme-static/03.png)\n\n### TODO\n\n- [-] Crear componenete `AddRecipe`.\n- [-] Agregar campo `title` al formulario.\n- [-] Agregar campo `vegetarian` de tipo *checkbox* al formulario.\n- [-] Actualizar el estado con los valores del formulario.\n- [-] **BONUS!!** Usar [React Hooks](https://reactjs.org/docs/hooks-reference.html).\n\nSi hace clic en el botón Agregar, no ocurrirá nada en este momento, excepto hacer que los campos se limpien.\n\n**Implementamos mutaciones básicas.**\n\nVamos a enviar la nueva información de la receta(formulario que debimos crear previamente) a nuestro back-end GraphQL. Vamos a nuestro componente `AddReecipe` y al principio importamos \u001c`mutation`\n\n```js\nimport { mutation } from \"react-apollo\";\n```\n\nLuego envolvemos nuestro formulario con este componente. Tiene un prop obligatoria, que es `mutation`. En nuestro caso, queremos agregar un `addRecipe` ya que nuestro backend asi lo estableció.\n\n```jsx\nrender() {\n  return (\n    \u003cMutation mutation={ADD_RECIPE_MUTATION}\u003e\n    ...\n    \u003c/mutation\u003e\n  )\n}\n```\n\nDado que todavía no tenemos tal mutación, necesitamos crearla. Al igual que con las consultas, vamos a utilizar el template tag `gql`.\n\n```js\nconst ADD_RECIPE_MUTATION = gql`\n  mutation addRecipe($recipe: RecipeInput!) {\n    addRecipe(recipe: $recipe)\n    {\n      id\n      title\n    }\n}\n`\n```\n\nAhora que tenemos nuestra mutación lista, usémosla. El *componenet child*  de `mutation` debe ser exactamente una función. Esto es llamado *render prop*. El llamado con la función de mutar que llamada `addRecipe` de acuerdo a la definición del backend. El segundo argumento es un objeto que contiene un resultado de `mutation`, así como el estado de `loading` y `error`.\n\n```jsx\nrender() {\n  return (\n    \u003cMutation mutation={ADD_RECIPE_MUTATION}\u003e {(addRecipe, { loading, error })\n\n    ...\n```\n\nUna vez que se envía el formulario, podemos usar nuestra función `addRecipe` para desencadenar la `\bmutation` y pasar el objeto de la receta, que contiene la propiedad `title` y `vegetarian`.\n\n```jsx\n{(addRecipe, { loading, error }) =\u003e (\n  \u003cform\n    onSubmit={evt =\u003e {\n      evt.preventDefault();\n      addRecipe({\n        variables: {\n          recipe: { title: this.state.title, vegetarian: this.state.vegetarian }\n        }\n      });\n    }}\n```\n¿Terminamos? Aún no. A favor de un buen UX, también debemos indicar el estado de carga e informar al usuario, en caso de que se produzca un error. Esto es todo lo que necesitamos para implementar la mutación.\n\n```jsx\n\u003cdiv\u003e\n  \u003cbutton\u003eAdd Recipe\u003c/button\u003e\n  {loading \u0026\u0026 \u003cp\u003eLoading...\u003c/p\u003e}\n  {error \u0026\u0026 \u003cp\u003eError :( Please try again\u003c/p\u003e}\n\u003c/div\u003e\n```\n\nPodemos ir al navegador y hacer clic en el botón *Add Recipe* para enviar el formulario. Aunque estoy seguro de que nuestra mutación **tuvo éxito**, no vemos reflejado nuestra nueva receta en la lista.\n\nEste es el causado porque la consulta que trae la lista de recetas **se encuentra en otro componente**. De ninguna manera indicamos que esta lista debería actualizarse una vez que la mutación haya finalizado. Podemos verificar rápidamente que nuestra mutación tuvo éxito solo con actualizar la página.\n\nAhora, queremos actualizar la lista con la mutación. Por lo tanto, podemos usar la prop `refetchQueries` en el componente `Mutation`. Esto acepta una serie de consultas, que se volverán a ejecutar una vez que la mutación haya tenido éxito. Vamos a proporcionar nuestra consulta de `recipes`.\n\n```jsx\nrender() {\n  return (\n    \u003cMutation\n      mutation={ADD_RECIPE_MUTATION}\n      refetchQueries={[\n        {\n          query: gql`\n            query recipes {\n              recipes {\n                id\n                title\n              }\n            }\n        } `\n      ]}\n    \u003e\n    {(addRecipe, { loading, error })\n...\n```\n\nDesafortunadamente, esto no funcionará, porque la consulta en el componente `Recipes` acepta una variable `vegetarian` y por lo tanto, es diferente.\n\nEsto significa que debemos pasar exactamente la misma consulta, con exactamente las mismas variables. Si bien podríamos copiarlo y pegarlo ahora, en este punto, probablemente sea mejor si exportamos la consulta y la importamos en este archivo, así como en la receta.\n\n```js\nexport const QUERY = gql`\n  query recipes($vegetarian: Boolean!) {\n    recipes(vegetarian: $vegetarian) {\n      id\n      title\n    }\n  }\n`;\n```\n\nLuego usamos las consultas de recetas dentro de la `refetchQueries`, donde pasamos un objecto vegetarianos y otro que no lo son.\n\n```jsx\n\u003cMutation\n  mutation={ADD_RECIPE_MUTATION}\n  refetchQueries={[\n    { query: QUERY, variables: { vegetarian: false } },\n    { query: QUERY, variables: { vegetarian: true } },\n  ]}\n\u003e\n```\n\nVamos a intentarlo. Añadimos una nueva receta. Luego veremos cómo se activan las `refetchQueries` después de que la mutación haya finalizado con éxito. Esto, sin embargo, podría no ser la experiencia de usuario deseada que está buscando. Personalmente prefiero si el indicador de carga permanece activo hasta que se actualicen las `refetchQueries`. Afortunadamente, este comportamiento es trivial de implementar simplemente agregando un prop `awaitRefetchQueries` y configurándolo en `true`.\n\n```jsx\n\u003cMutation\n  mutation={ADD_RECIPE_MUTATION}\n  refetchQueries={[\n    { query: QUERY, variables: { vegetarian: false } },\n    { query: QUERY, variables: { vegetarian: true } },\n  ]}\n  awaitRefetchQueries={true}\n\u003e\n```\n\nVamos a actualizar la página. Luego añadimos otro plato vegetariano. Como puede ver, esta receta apareció en la lista al mismo tiempo que desaparecía el indicador de carga. Si activamos el filtro vegetariano, la lista se procesa instantáneamente, ya que ya hemos actualizado el caché usando `refetchQueries`.\n\nAl principio es molesto tener que lidiar con la cache que se genera con Apollo Client. Si desea comenzar de manera simple, he visto a los desarrolladores hacer esto un par de veces y es desactivar el caché de Apollo de forma predeterminada y solo usarlo explícitamente, en caso de que sus optimizaciones tengan un gran impacto en la experiencia del usuario.\n\n## Manage Local State using Apollo by extending the GraphQL Schema on the Client\n\nCon la introducción de `apollo-link-state`, Apollo introdujo por primera vez una forma de administrar el estado local a través de consultas y mutaciones de GraphQL. Esto se puede lograr utilizando la directiva `@client`. En esta lección, aprovecharemos esta función para realizar un seguimiento de las recetas destacadas y almacenar la información en el `localStorage`.\n\nEn esta lección, queremos extender nuestra aplicación para permitir a los usuarios marcar sus recetas favoritas. Por lo tanto, queremos agregar un campo `isStarred` a la receta. Dado que nuestro esquema no admite este campo, planeamos mantener esta información únicamente en el cliente, almacenado unicamente en local.\n\nPara consultar un campo solo en el cliente, podemos aprovechar el decorador `@client` y agregarlo a un campo en una consulta o una mutación.\n\n```js\nimport gql from \"graphql-tag\";\n\nexport const GET_RECIPES = gql`\n  query recipes($vegetarian: Boolean!) {\n    recipes(vegetarian: $vegetarian) {\n      id\n      title\n      isStarred @client\n    }\n  }\n`;\n```\nUna vez agregado, este campo nunca se consulta en el punto final remoto, sin embargo, necesitamos proporcionar un resolver para esto. En nuestro archivo `app.js`, tendremos un objecto `resolver`  y para el tipo `recipe` agregue un resolver `isStarred`. Temporalmente vamos a devolver el valor `false` para todos los `recipes`.\n\n```js\nconst resolvers = {\n  Recipe: {\n    isStarred: parent =\u003e false\n  }\n}\n```\n\nLuego podemos usar el objeto del *resolver* y pasarlo a la propiedad `clientState` durante la inicialización de `ApolloClient`.\n\n```js\nconst client = new ApolloClient({\n  uri: \"http://localhost:4000/\",\n  clientState: {\n    resolvers\n  }\n});\n```\nEsto es todo lo que necesitamos para poder recuperar el valor `isStarred` en nuestra lista de `Recipes.js`. Procesamos una estrella junto a cada uno de los títulos, y el color debería cambiar, según el estado. Naranja, si la receta está marcada y gris si no lo está.\n\n```jsx\nreturn (\n  \u003cul\u003e\n    {data.recipes.map(({ id, title, isStarred }) =\u003e (\n      \u003cli key={id}\u003e\n        {title}\n        \u003cspan style={{ color: isStarred ? \"orange\" : \"grey\" }}\u003e\n          ★\n        \u003c/span\u003e\n      \u003c/li\u003e\n```\nActualicemos la página y verifiquemos que todas las estrellas estén inactivas.\n\n![Recipe stars](./.readme-static/04.png)\n\nA continuación, necesitamos una mutación que nos permita actualizar el campo `isStarred` de una receta. Por lo tanto, extendemos nuestros *resolvers* del lado del cliente con una mutación que vamos a llamar `updateRecipeStarred`. Nuestro plan es almacenar un array de recetas destacadas en el `localstorage`. Antes de actualizarlo, recuperamos la lista.\n\n```js\nMutation: {\n  updateRecipeStarred: (_, variables) =\u003e {\n    const starredRecipes = JSON.parse(localStorage.getItem(\"starredRecipes\")) || [];\n  }\n}\n\n// Signature\n// fieldName(obj, args, context, info) { result }\n```\nPara conocer la firma de los resolvers puede ir [aquí](https://www.apollographql.com/docs/graphql-tools/resolvers.html#Resolver-function-signature)\n\nEn caso de que la variable `isStarred` se establezca en `true`, añadimo el `id` actual al *localstorage*. En caso de que esté configurado como `false`, filtramos la lista actual de ID's del *localstorage* para guardar de nuevo los ID's sin el actual.\n\n```js\nMutation: {\n  updateRecipeStarred: (_, variables) =\u003e {\n    const starredRecipes = JSON.parse(localStorage.getItem(\"starredRecipes\")) || [];\n\n    if (variables.isStarred) {\n      const addItem = JSON.stringify(starredRecipes.concat([variables.id]))\n      localStorage.setItem(\"starredRecipes\", addItem);\n    } else {\n      const removeItem = JSON.stringify(starredRecipes.filter(recipeId =\u003e recipeId !== variables.id))\n      localStorage.setItem(\"starredRecipes\", removeItem);\n    }\n\n  }\n}\n```\n\nAl final, devolvemos un objeto con `__typename` y el valor `isStarred`. Esto es útil en caso de que un desarrollador quiera consultar el valor actualizado.\n\n```js\nMutation: {\n  updateRecipeStarred: (_, variables) =\u003e {\n    ...\n\n    return {\n      __typename: \"Recipe\",\n      isStarred: variables.isStarred\n    };\n  }\n}\n```\n\nComo ahora tenemos la mutación y sabemos cómo almacenamos las recetas destacadas, también puede actualizar el codigo del resolver de `recipes`. En el *resolver*, verificamos si el array `starredRecipes` incluye el ID de la receta `parent.id` actual y devolvemos el resultado.\n\n```js\nconst resolvers = {\n  Recipe: {\n    isStarred: parent =\u003e {\n      const starredRecipes = JSON.parse(localStorage.getItem(\"starredRecipes\")) || [];\n      return starredRecipes.includes(parent.id);\n    }\n  },\n```\n\nHasta este punto solo estabamos preparando el terreno, ahora tenemos todo lo que necesitamos para comenzar a usar nuestra mutación. Por lo tanto, agregamos una nueva mutación al archivo `src/components/Recipes.js`.\n\n```js\nconst UPDATE_RECIPE_STARRED_MUTATION = gql`\n  mutation updateRecipeStarred($id: ID!, $isStarred: Boolean!) {\n    updateRecipeStarred(id: $id, isStarred: $isStarred) @client\n  }\n`;\n```\n\nDespués de eso, importamos el componente `Mutation` y envolvemos el `span` con la estrella con él.\n\n```js\nimport { Query, Mutation } from \"react-apollo\";\n```\n\nLuego pasamos nuestro `UPDATE_RECIPE_STARRED_MUTATION`. Dado que esta mutación afecta el resultado de nuestra consulta de recetas, proporcionamos los dos `refetchQeries`.\n\n```jsx\n\u003cMutation\n  mutation={UPDATE_RECIPE_STARRED_MUTATION}\n  refetchQueries={[\n    { query: GET_RECIPES, variables: { vegetarian: false } },\n    { query: GET_RECIPES, variables: { vegetarian: true } }\n  ]}\n  awaitRefetchQueries={true}\n\u003e\n```\nUna vez más, establezca `awaitRefetchQueries` en verdadero. Después de eso, reemplazamos el `\u003cspan\u003e` con un `\u003cbutton\u003e` y agregamos un evento `onClick`. Una vez que se hace clic, invocamos la mutación con el `ID` de receta actual y el valor inverso `!IsStarred`.\n\n```jsx\n\u003cMutation\n  mutation={UPDATE_RECIPE_STARRED_MUTATION}\n  refetchQueries={[\n   ...\n  ]}\n  awaitRefetchQueries={true}\n\u003e\n  {\n    (updateRecipeStarred, { loading, error }) =\u003e (\n      \u003cbutton\n        className=\"star-btn\"\n        style={{...}}\n        onClick={() =\u003e\n          updateRecipeStarred({\n            variables: { id, isStarred: !isStarred }\n          })\n        }\n      \u003e\n        ★\n      \u003c/button\u003e\n    )\n  }\n\u003c/Mutation\u003e\n```\n\nA continuación, agregamos una clase llamada `star-btn`. Para asegurarme de que la estrella se vea realmente bien, agregué la clase al archivo `index.html`.\n\n```html\n\u003cstyle\u003e\n  .star-btn {\n    position: absolute;\n    padding: 0;\n    margin: 0 0 0 0.3rem;\n    height: 1.4rem;\n    line-height: 1.4rem;\n    background: none;\n    border: 0;\n    font-size: 1rem;\n    outline: 0\n  }\n  .star-btn:hover {\n    background: none;\n    opacity: 0.8;\n  }\n\n  .star-btn:focus {\n    box-shadow: none;\n  }\n\n  @keyframes inflate {\n    0% {\n      font-size: 1rem;\n      margin-left: 0.3rem;\n    }\n    100% {\n      font-size: 1.4rem;\n      margin-left: 0.1rem;\n    }\n  }\n\u003c/style\u003e\n```\n\nDependiendo del estado `loading`, agregamos la `animation`. Por último, pero no menos importante, renderizar texto en caso de que se produzca un `error`. Ahora, deberíamos poder comenzar nuestras recetas.\n\n```jsx\n(updateRecipeStarred, { loading, error }) =\u003e (\n  \u003cbutton\n    className=\"star-btn\"\n    style={{\n      color: isStarred ? \"orange\" : \"grey\",\n      animation: loading ? \"inflate 0.7s ease infinite alternate\" : \"none\"\n    }}\n    onClick={() =\u003e\n      updateRecipeStarred({\n        variables: { id, isStarred: !isStarred }\n      })\n    }\n  \u003e\n    ★ {error \u0026\u0026 \"Failed to update\"}\n  \u003c/button\u003e\n)\n```\n\nActualizamos la página y le damos una oportunidad. Voila, funciona como un amuleto. Incluso funciona después de comprobar el filtro vegetariano.\n\n## Refetch Data with the Apollo Query Component either Manually or on Timed Intervals\nEn esta lección, primero exploraremos cómo recuperar manualmente los datos de una consulta ya ejecutada para recibir el estado más reciente de nuestros datos. Más tarde, utilizamos el *polling* para recuperar el último estado en un intervalo de tiempo. El *polling* puede ser una herramienta simple y efectiva para proporcionar una experiencia casi en tiempo real sin la sobrecarga de configurar una solución WebSocket.\n\nHasta ahora, utilizamos `data`, `loading`, `error` desde la función *render prop* de un componente de `Query`. Hay un par de utilidades más que vienen con este objeto. Uno de ellos es `refetch`.\n\n```jsx\n\u003cQuery query={GET_RECIPES} variables={{ vegetarian: checked.vegetarian }}\u003e\n  {\n    ({ data, loading, error, refetch }) =\u003e {\n      if (loading) return \u003cp\u003eLoading…\u003c/p\u003e;\n      if (error) return \u003cp\u003eSomething went wrong\u003c/p\u003e;\n\n      return (\n        ...\n      )\n  }\n\u003c/Query\u003e\n```\n\nUna vez invocado, se volverá a ejecutar la consulta. Vamos a intentarlo agregando un `\u003cbutton\u003e` para actualizar las recetas.\n\n```jsx\n\u003cReact.Fragment\u003e\n  ...\n\n  \u003cbutton onClick={() =\u003e refetch()}\u003e Refresh Recipes\u003c/button\u003e\n\u003c/React.Fragment\u003e\n```\n\nUna vez implementado, agregamos un nuevo elemento en otro navegador, volvemos a nuestro anterior tab y ejecutamos la recuperación presionando el botón. Como era de esperar, ahora vemos la nueva receta que se está agregando. Otros ejemplos de utilidades que vienen dentro del *render prop* del componente `Query` son: *network status*, obtener más datos con *pagination* o iniciar y detener el *polling*.\n\nDicho esto, la forma más fácil de lograr el *polling* para obtener nuevos resultados es usar la prop `pollInterval` en el propio componente de consulta. De forma predeterminada, está desactivado, pero si proporciona un número como `{3000}`, el componente volverá a ejecutar la consulta cada tres segundos.\n\n```jsx\n\u003cQuery\n  query={GET_RECIPES}\n  variables={{ vegetarian: checked.vegetarian }}\n  pollInterval={3000}\n\u003e\n```\n\nPuedes probar creando otra nueva receta en otra pestaña y como puedes ver en la pestaña anterior del navegador, se puede ver en pocos segundos esta nueva recenta sin hacer un web socket, el *polling* puede ser una herramienta simple y efectiva para proporcionar una experiencia en tiempo casi real.\n\nCon esto llegamos al final de este taller basico sobre **Apollo Client**\n\n**¡Espero que esto haya sido útil y/o te haya hecho aprender algo nuevo!**\n\n\n## Author\n\n* **Cristian Moreno** - *FullStack JavaScript Developer* - [Github](https://github.com/khriztianmoreno)\nSee also the list of [contributors](https://github.com/khriztianmoreno/apollo-client-workshop-react/contributors) who participated in this project.\n\n## License\n\nThis project is licensed under Copy Right - see the [LICENSE](LICENSE) file for details\n\n## References\n\n- [apollo-boost](https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost)\n- [graphql-tag](https://github.com/apollographql/graphql-tag)\n- [React context](https://reactjs.org/docs/context.html)\n- [render props](https://reactjs.org/docs/render-props.html)\n- [Caching data](https://www.apollographql.com/docs/react/advanced/caching.html)\n- [React Hooks](https://reactjs.org/docs/hooks-reference.html)\n- [Apollo Query props](https://www.apollographql.com/docs/react/essentials/queries.html#propsgit )\n- [Resolvers](https://www.apollographql.com/docs/graphql-tools/resolvers.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhriztianmoreno%2Fapollo-client-workshop-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhriztianmoreno%2Fapollo-client-workshop-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhriztianmoreno%2Fapollo-client-workshop-react/lists"}