Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

(examples) - Migrate to Vite and update some examples (#1630)

authored by

Phil Pluckthun and committed by
GitHub
4c1a9d9b 8ec778d2

+1041 -875
+3 -3
examples/with-apq/README.md
··· 19 19 20 20 This example contains: 21 21 22 - - The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.js) 23 - - The `persistedFetchExchange` from `@urql/exchange-persisted-fetch` in [`src/App.js`](src/App.js) 24 - - A query for locations in [`src/pages/LocationsList.js`](src/pages/LocationsList.js) 22 + - The `urql` bindings and a React app with a client set up in [`src/App.jsx`](src/App.jsx) 23 + - The `persistedFetchExchange` from `@urql/exchange-persisted-fetch` in [`src/App.jsx`](src/App.jsx) 24 + - A query for locations in [`src/LocationsList.jsx`](src/pages/LocationsList.jsx)
+27
examples/with-apq/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-apq</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-apq/package.json
··· 2 2 "name": "with-apq", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "@urql/exchange-persisted-fetch": "^1.3.0", 7 10 "graphql": "^15.5.0", ··· 10 13 "urql": "^2.0.2" 11 14 }, 12 15 "devDependencies": { 13 - "react-scripts": "4.0.3" 14 - }, 15 - "scripts": { 16 - "start": "react-scripts start", 17 - "build": "react-scripts build", 18 - "test": "react-scripts test", 19 - "eject": "react-scripts eject" 20 - }, 21 - "browserslist": { 22 - "production": [ 23 - ">0.2%", 24 - "not dead", 25 - "not op_mini all" 26 - ], 27 - "development": [ 28 - "last 1 chrome version", 29 - "last 1 firefox version", 30 - "last 1 safari version" 31 - ] 16 + "@vitejs/plugin-react-refresh": "^1.3.3", 17 + "vite": "^2.2.4" 32 18 } 33 19 }
+5 -4
examples/with-apq/src/App.js examples/with-apq/src/App.jsx
··· 1 + import React from 'react'; 1 2 import { createClient, Provider, fetchExchange } from 'urql'; 2 3 import { persistedFetchExchange } from '@urql/exchange-persisted-fetch'; 3 4 4 - import LocationsList from "./pages/LocationsList"; 5 + import LocationsList from './LocationsList'; 5 6 6 7 const client = createClient({ 7 - url: "https://trygql.formidable.dev/graphql/apq-weather", 8 + url: 'https://trygql.formidable.dev/graphql/apq-weather', 8 9 exchanges: [ 9 10 persistedFetchExchange({ 10 11 preferGetForPersistedQueries: true, 11 12 }), 12 - fetchExchange 13 - ] 13 + fetchExchange, 14 + ], 14 15 }); 15 16 16 17 function App() {
-13
examples/with-apq/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-apq/src/index.js examples/with-graphcache-pagination/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
+7 -4
examples/with-apq/src/pages/LocationsList.js examples/with-apq/src/LocationsList.jsx
··· 1 - import React from "react"; 2 - import { gql, useQuery } from "urql"; 1 + import React from 'react'; 2 + import { gql, useQuery } from 'urql'; 3 3 4 4 const LOCATIONS_QUERY = gql` 5 5 query Locations($query: String!) { ··· 11 11 `; 12 12 13 13 const LocationsList = () => { 14 - const [result] = useQuery({ query: LOCATIONS_QUERY, variables: { query: "LON"} }); 14 + const [result] = useQuery({ 15 + query: LOCATIONS_QUERY, 16 + variables: { query: 'LON' }, 17 + }); 15 18 16 19 const { data, fetching, error } = result; 17 20 ··· 23 26 24 27 {data && ( 25 28 <ul> 26 - {data.locations.map((location) => ( 29 + {data.locations.map(location => ( 27 30 <li key={location.id}>{location.name}</li> 28 31 ))} 29 32 </ul>
+7
examples/with-apq/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+27
examples/with-graphcache-pagination/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-graphcache-pagination</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-graphcache-pagination/package.json
··· 2 2 "name": "with-graphcache-pagination", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "@urql/exchange-graphcache": "^4.0.0", 7 10 "graphql": "^15.5.0", ··· 10 13 "urql": "^2.0.2" 11 14 }, 12 15 "devDependencies": { 13 - "react-scripts": "4.0.3" 14 - }, 15 - "scripts": { 16 - "start": "react-scripts start", 17 - "build": "react-scripts build", 18 - "test": "react-scripts test", 19 - "eject": "react-scripts eject" 20 - }, 21 - "browserslist": { 22 - "production": [ 23 - ">0.2%", 24 - "not dead", 25 - "not op_mini all" 26 - ], 27 - "development": [ 28 - "last 1 chrome version", 29 - "last 1 firefox version", 30 - "last 1 safari version" 31 - ] 16 + "@vitejs/plugin-react-refresh": "^1.3.3", 17 + "vite": "^2.2.4" 32 18 } 33 19 }
+6 -5
examples/with-graphcache-pagination/src/App.js examples/with-graphcache-pagination/src/App.jsx
··· 1 - import { createClient, Provider, dedupExchange, fetchExchange } from "urql"; 1 + import React from 'react'; 2 + import { createClient, Provider, dedupExchange, fetchExchange } from 'urql'; 2 3 import { cacheExchange } from '@urql/exchange-graphcache'; 3 4 import { relayPagination } from '@urql/exchange-graphcache/extras'; 4 5 5 - import PaginatedNpmSearch from "./pages/PaginatedNpmSearch"; 6 + import PaginatedNpmSearch from './PaginatedNpmSearch'; 6 7 7 8 const client = createClient({ 8 - url: "https://trygql.formidable.dev/graphql/relay-npm", 9 + url: 'https://trygql.formidable.dev/graphql/relay-npm', 9 10 exchanges: [ 10 11 dedupExchange, 11 12 cacheExchange({ ··· 15 16 }, 16 17 }, 17 18 }), 18 - fetchExchange 19 - ] 19 + fetchExchange, 20 + ], 20 21 }); 21 22 22 23 function App() {
-13
examples/with-graphcache-pagination/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-graphcache-pagination/src/index.js examples/with-graphcache-updates/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
+9 -8
examples/with-graphcache-pagination/src/pages/PaginatedNpmSearch.js examples/with-graphcache-pagination/src/PaginatedNpmSearch.jsx
··· 1 - import React, { useState } from "react"; 2 - import { gql, useQuery } from "urql"; 1 + import React, { useState } from 'react'; 2 + import { gql, useQuery } from 'urql'; 3 3 4 4 const limit = 5; 5 - const query = "graphql"; 5 + const query = 'graphql'; 6 6 7 7 const NPM_SEARCH = gql` 8 8 query Search($query: String!, $first: Int!, $after: String) { ··· 21 21 } 22 22 `; 23 23 24 - 25 24 const PaginatedNpmSearch = () => { 26 - const [after, setAfter] = useState(""); 25 + const [after, setAfter] = useState(''); 27 26 28 27 const [result] = useQuery({ 29 - query: NPM_SEARCH, 30 - variables: { query, first: limit, after } 28 + query: NPM_SEARCH, 29 + variables: { query, first: limit, after }, 31 30 }); 32 31 33 32 const { data, fetching, error } = result; ··· 43 42 {searchResults && ( 44 43 <> 45 44 {searchResults.edges.map(({ node }) => ( 46 - <div key={node.id}>{node.id}: {node.name}</div> 45 + <div key={node.id}> 46 + {node.id}: {node.name} 47 + </div> 47 48 ))} 48 49 49 50 {searchResults.pageInfo.hasNextPage && (
+7
examples/with-graphcache-pagination/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+3 -3
examples/with-graphcache-updates/README.md
··· 19 19 20 20 This example contains: 21 21 22 - - The `urql` bindings and a React app with a client set up in [`src/client/index.js`](src/client/index.js) 23 - - The `cacheExchange` from `@urql/exchange-graphcache` in [`src/client/index.js`](src/client/index.js) 24 - - A links list and link creation in [`src/pages/Links.js`](src/pages/Links.js) 22 + - The `urql` bindings and a React app with a client set up in [`src/client.js`](src/client.js) 23 + - The `cacheExchange` from `@urql/exchange-graphcache` in [`src/client.js`](src/client.js) 24 + - A links list and link creation in [`src/pages/Links.jsx`](src/pages/Links.jsx)
+27
examples/with-graphcache-updates/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-graphcache-updates</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-graphcache-updates/package.json
··· 2 2 "name": "with-graphcache-updates", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "@urql/exchange-auth": "^0.1.2", 7 10 "@urql/exchange-graphcache": "^4.0.0", ··· 11 14 "urql": "^2.0.2" 12 15 }, 13 16 "devDependencies": { 14 - "react-scripts": "4.0.3" 15 - }, 16 - "scripts": { 17 - "start": "react-scripts start", 18 - "build": "react-scripts build", 19 - "test": "react-scripts test", 20 - "eject": "react-scripts eject" 21 - }, 22 - "browserslist": { 23 - "production": [ 24 - ">0.2%", 25 - "not dead", 26 - "not op_mini all" 27 - ], 28 - "development": [ 29 - "last 1 chrome version", 30 - "last 1 firefox version", 31 - "last 1 safari version" 32 - ] 17 + "@vitejs/plugin-react-refresh": "^1.3.3", 18 + "vite": "^2.2.4" 33 19 } 34 20 }
-14
examples/with-graphcache-updates/src/App.js
··· 1 - import { Provider } from "urql"; 2 - 3 - import client from "./client"; 4 - import Home from "./pages/Home"; 5 - 6 - function App() { 7 - return ( 8 - <Provider value={client}> 9 - <Home /> 10 - </Provider> 11 - ); 12 - } 13 - 14 - export default App;
+33
examples/with-graphcache-updates/src/App.jsx
··· 1 + import React, { useState, useEffect } from 'react'; 2 + import { Provider } from 'urql'; 3 + 4 + import client from './client'; 5 + import Links from './pages/Links'; 6 + import LoginForm from './pages/LoginForm'; 7 + 8 + const Home = () => { 9 + const [isLoggedIn, setIsLoggedIn] = useState(false); 10 + 11 + const onLoginSuccess = auth => { 12 + localStorage.setItem('authToken', auth.token); 13 + setIsLoggedIn(true); 14 + }; 15 + 16 + useEffect(() => { 17 + if (localStorage.getItem('authToken')) { 18 + setIsLoggedIn(true); 19 + } 20 + }, []); 21 + 22 + return isLoggedIn ? <Links /> : <LoginForm onLoginSuccess={onLoginSuccess} />; 23 + }; 24 + 25 + function App() { 26 + return ( 27 + <Provider value={client}> 28 + <Home /> 29 + </Provider> 30 + ); 31 + } 32 + 33 + export default App;
examples/with-graphcache-updates/src/client/index.js examples/with-graphcache-updates/src/client.js
-13
examples/with-graphcache-updates/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-graphcache-updates/src/index.js examples/with-multipart/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
-27
examples/with-graphcache-updates/src/pages/Home.js
··· 1 - import React, { useEffect, useState } from 'react'; 2 - 3 - import Links from './Links'; 4 - import LoginForm from './LoginForm'; 5 - 6 - const Home = () => { 7 - const [isLoggedIn, setIsLoggedIn] = useState(false); 8 - 9 - const onLoginSuccess = auth => { 10 - localStorage.setItem("authToken", auth.token); 11 - setIsLoggedIn(true); 12 - }; 13 - 14 - useEffect(() => { 15 - if (localStorage.getItem("authToken")) { 16 - setIsLoggedIn(true); 17 - } 18 - }, []); 19 - 20 - return isLoggedIn ? ( 21 - <Links /> 22 - ) : ( 23 - <LoginForm onLoginSuccess={onLoginSuccess} /> 24 - ); 25 - }; 26 - 27 - export default Home;
-74
examples/with-graphcache-updates/src/pages/Links.js
··· 1 - import React, {useEffect, useState} from "react"; 2 - import { gql, useQuery, useMutation } from "urql"; 3 - 4 - const LINKS_QUERY = gql` 5 - query Links($first: Int!) { 6 - links(first: $first) { 7 - nodes { 8 - id 9 - canonicalUrl 10 - } 11 - } 12 - } 13 - `; 14 - 15 - const CREATE_LINK_MUTATION = gql` 16 - mutation CreateLink($url: URL!) { 17 - createLink(url: $url) { 18 - node { 19 - id 20 - canonicalUrl 21 - } 22 - } 23 - } 24 - `; 25 - 26 - const Links = () => { 27 - const [newLink, setNewLink] = useState(""); 28 - 29 - const [linksResult] = useQuery({ query: LINKS_QUERY, variables: { first: 10 } }); 30 - const [createResult, createLink] = useMutation(CREATE_LINK_MUTATION) 31 - 32 - const onChangeLink = evt => { 33 - setNewLink(evt.target.value); 34 - } 35 - 36 - const onSubmit = evt => { 37 - evt.preventDefault(); 38 - createLink({ url: newLink }); 39 - }; 40 - 41 - useEffect(() => { 42 - if (createResult.data) { 43 - setNewLink(""); 44 - } 45 - }, [createResult]); 46 - 47 - if (linksResult.fetching || createResult.fetching) { 48 - return <p>Loading...</p> 49 - } 50 - 51 - return ( 52 - <div> 53 - {linksResult.error && <p>Oh no... {linksResult.error.message}</p>} 54 - 55 - {linksResult.data && ( 56 - <ul> 57 - {linksResult.data.links.nodes.map((link) => ( 58 - <li key={link.id}>{link.canonicalUrl}</li> 59 - ))} 60 - </ul> 61 - )} 62 - 63 - <form onSubmit={onSubmit}> 64 - {createResult.error && <p>Oh no... {createResult.error.message}</p>} 65 - 66 - <input onChange={onChangeLink} value={newLink} /> 67 - 68 - <input type="submit" title="add" /> 69 - </form> 70 - </div> 71 - ); 72 - }; 73 - 74 - export default Links;
+77
examples/with-graphcache-updates/src/pages/Links.jsx
··· 1 + import React from 'react'; 2 + import { gql, useQuery, useMutation } from 'urql'; 3 + 4 + const LINKS_QUERY = gql` 5 + query Links($first: Int!) { 6 + links(first: $first) { 7 + nodes { 8 + id 9 + title 10 + canonicalUrl 11 + } 12 + } 13 + } 14 + `; 15 + 16 + const CREATE_LINK_MUTATION = gql` 17 + mutation CreateLink($url: URL!) { 18 + createLink(url: $url) { 19 + node { 20 + id 21 + title 22 + canonicalUrl 23 + } 24 + } 25 + } 26 + `; 27 + 28 + const Links = () => { 29 + const [linksResult] = useQuery({ 30 + query: LINKS_QUERY, 31 + variables: { first: 10 }, 32 + }); 33 + const [createResult, createLink] = useMutation(CREATE_LINK_MUTATION); 34 + 35 + const onSubmitLink = event => { 36 + event.preventDefault(); 37 + const { target } = event; 38 + createLink({ url: new FormData(target).get('link') }).then(() => 39 + target.reset() 40 + ); 41 + }; 42 + 43 + return ( 44 + <div> 45 + {linksResult.error && <p>Oh no... {linksResult.error.message}</p>} 46 + 47 + {linksResult.data && ( 48 + <ul> 49 + {linksResult.data.links.nodes.map(link => ( 50 + <li key={link.id}> 51 + <a rel="noreferrer" href={link.canonicalUrl}> 52 + {link.title} 53 + </a> 54 + </li> 55 + ))} 56 + </ul> 57 + )} 58 + 59 + <form onSubmit={onSubmitLink}> 60 + {createResult.fetching ? <p>Submitting...</p> : null} 61 + {createResult.error ? ( 62 + <p>Oh no... {createResult.error.message}</p> 63 + ) : null} 64 + 65 + <fieldset disabled={createResult.fetching ? 'disabled' : null}> 66 + <label> 67 + {'Link to Blog Post: '} 68 + <input type="url" name="link" placeholder="https://..." /> 69 + </label> 70 + <button type="submit">Add Link</button> 71 + </fieldset> 72 + </form> 73 + </div> 74 + ); 75 + }; 76 + 77 + export default Links;
-57
examples/with-graphcache-updates/src/pages/LoginForm.js
··· 1 - import React, { useEffect, useState } from 'react'; 2 - import { gql, useMutation } from 'urql'; 3 - 4 - const LOGIN_MUTATION = gql` 5 - mutation Signin($input: LoginInput!) { 6 - signin(input: $input) { 7 - refreshToken 8 - token 9 - } 10 - } 11 - `; 12 - 13 - const LoginForm = ({ onLoginSuccess }) => { 14 - const [password, setPassword] = useState(''); 15 - const [username, setUsername] = useState(''); 16 - 17 - const [loginResult, login] = useMutation(LOGIN_MUTATION); 18 - 19 - const { data, fetching, error } = loginResult; 20 - 21 - const onUsernameChange = evt => { 22 - setUsername(evt.target.value); 23 - }; 24 - 25 - const onPasswordChange = evt => { 26 - setPassword(evt.target.value); 27 - }; 28 - 29 - const onSubmit = evt => { 30 - evt.preventDefault(); 31 - login({ input: { username, password } }); 32 - }; 33 - 34 - useEffect(() => { 35 - if (data && data.signin) { 36 - onLoginSuccess(data.signin); 37 - } 38 - }, [onLoginSuccess, data]); 39 - 40 - if (fetching) { 41 - return <p>loading...</p>; 42 - } 43 - 44 - return ( 45 - <form onSubmit={onSubmit}> 46 - {error && <p>Oh no... {error.message}</p>} 47 - 48 - <input type="text" onChange={onUsernameChange} /> 49 - 50 - <input type="password" onChange={onPasswordChange} /> 51 - 52 - <input type="submit" title="login" /> 53 - </form> 54 - ); 55 - }; 56 - 57 - export default LoginForm;
+101
examples/with-graphcache-updates/src/pages/LoginForm.jsx
··· 1 + import React from 'react'; 2 + import { gql, useMutation } from 'urql'; 3 + 4 + const LOGIN_MUTATION = gql` 5 + mutation Login($input: LoginInput!) { 6 + signin(input: $input) { 7 + refreshToken 8 + token 9 + } 10 + } 11 + `; 12 + 13 + const REGISTER_MUTATION = gql` 14 + mutation Register($input: LoginInput!) { 15 + register(input: $input) { 16 + refreshToken 17 + token 18 + } 19 + } 20 + `; 21 + 22 + const LoginForm = ({ onLoginSuccess }) => { 23 + const [loginResult, login] = useMutation(LOGIN_MUTATION); 24 + const [registerResult, register] = useMutation(REGISTER_MUTATION); 25 + 26 + const onSubmitLogin = event => { 27 + event.preventDefault(); 28 + const data = new FormData(event.target); 29 + const username = data.get('username'); 30 + const password = data.get('password'); 31 + 32 + login({ input: { username, password } }).then(result => { 33 + if (!result.error && result.data && result.data.signin) { 34 + onLoginSuccess(result.data.signin); 35 + } 36 + }); 37 + }; 38 + 39 + const onSubmitRegister = event => { 40 + event.preventDefault(); 41 + const data = new FormData(event.target); 42 + const username = data.get('username'); 43 + const password = data.get('password'); 44 + 45 + register({ input: { username, password } }).then(result => { 46 + if (!result.error && result.data && result.data.register) { 47 + onLoginSuccess(result.data.register); 48 + } 49 + }); 50 + }; 51 + 52 + const disabled = loginResult.fetching || registerResult.fetching; 53 + 54 + return ( 55 + <> 56 + <form onSubmit={onSubmitLogin}> 57 + {loginResult.fetching ? <p>Logging in...</p> : null} 58 + {loginResult.error ? <p>Oh no... {loginResult.error.message}</p> : null} 59 + 60 + <fieldset disabled={disabled ? 'disabled' : null}> 61 + <h3>Login</h3> 62 + <label> 63 + Username: 64 + <input name="username" type="text" /> 65 + </label> 66 + 67 + <label> 68 + Password: 69 + <input name="password" type="password" /> 70 + </label> 71 + 72 + <button type="submit">Login</button> 73 + </fieldset> 74 + </form> 75 + 76 + <form onSubmit={onSubmitRegister}> 77 + {registerResult.fetching ? <p>Signing up...</p> : null} 78 + {registerResult.error ? ( 79 + <p>Oh no... {registerResult.error.message}</p> 80 + ) : null} 81 + 82 + <fieldset disabled={disabled ? 'disabled' : null}> 83 + <h3>Register</h3> 84 + <label> 85 + {'Username: '} 86 + <input name="username" type="text" /> 87 + </label> 88 + 89 + <label> 90 + {'Password: '} 91 + <input name="password" type="password" /> 92 + </label> 93 + 94 + <button type="submit">Register</button> 95 + </fieldset> 96 + </form> 97 + </> 98 + ); 99 + }; 100 + 101 + export default LoginForm;
+7
examples/with-graphcache-updates/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+3 -3
examples/with-multipart/README.md
··· 17 17 18 18 This example contains: 19 19 20 - - The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.js) 21 - - The `multipartFetchExchange` from `@urql/exchange-multipart-fetch` in [`src/App.js`](src/App.js) 22 - - A basic file upload form in [`src/pages/FileUpload.js`](src/pages/FileUpload.js) 20 + - The `urql` bindings and a React app with a client set up in [`src/App.jsx`](src/App.jsx) 21 + - The `multipartFetchExchange` from `@urql/exchange-multipart-fetch` in [`src/App.jsx`](src/App.jsx) 22 + - A basic file upload form in [`src/FileUpload.jsx`](src/FileUpload.jsx)
+27
examples/with-multipart/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-multipart</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-multipart/package.json
··· 2 2 "name": "with-multipart", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "@urql/exchange-multipart-fetch": "^0.1.11", 7 10 "graphql": "^15.5.0", ··· 10 13 "urql": "^2.0.2" 11 14 }, 12 15 "devDependencies": { 13 - "react-scripts": "4.0.3" 14 - }, 15 - "scripts": { 16 - "start": "react-scripts start", 17 - "build": "react-scripts build", 18 - "test": "react-scripts test", 19 - "eject": "react-scripts eject" 20 - }, 21 - "browserslist": { 22 - "production": [ 23 - ">0.2%", 24 - "not dead", 25 - "not op_mini all" 26 - ], 27 - "development": [ 28 - "last 1 chrome version", 29 - "last 1 firefox version", 30 - "last 1 safari version" 31 - ] 16 + "@vitejs/plugin-react-refresh": "^1.3.3", 17 + "vite": "^2.2.4" 32 18 } 33 19 }
-20
examples/with-multipart/src/App.js
··· 1 - import { createClient, Provider } from "urql"; 2 - import { multipartFetchExchange } from "@urql/exchange-multipart-fetch"; 3 - 4 - import FileUpload from "./pages/FileUpload"; 5 - 6 - const client = createClient({ 7 - url: "https://trygql.formidable.dev/graphql/uploads-mock", 8 - exchanges: [multipartFetchExchange], 9 - ], 10 - }); 11 - 12 - function App() { 13 - return ( 14 - <Provider value={client}> 15 - <FileUpload /> 16 - </Provider> 17 - ); 18 - } 19 - 20 - export default App;
+20
examples/with-multipart/src/App.jsx
··· 1 + import React from 'react'; 2 + import { createClient, Provider } from 'urql'; 3 + import { multipartFetchExchange } from '@urql/exchange-multipart-fetch'; 4 + 5 + import FileUpload from './FileUpload'; 6 + 7 + const client = createClient({ 8 + url: 'https://trygql.formidable.dev/graphql/uploads-mock', 9 + exchanges: [multipartFetchExchange], 10 + }); 11 + 12 + function App() { 13 + return ( 14 + <Provider value={client}> 15 + <FileUpload /> 16 + </Provider> 17 + ); 18 + } 19 + 20 + export default App;
-13
examples/with-multipart/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-multipart/src/index.js examples/with-pagination/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
+8 -10
examples/with-multipart/src/pages/FileUpload.js examples/with-multipart/src/FileUpload.jsx
··· 1 - import React, { useState } from "react"; 2 - import { gql, useMutation } from "urql"; 1 + import React, { useState } from 'react'; 2 + import { gql, useMutation } from 'urql'; 3 3 4 4 const UPLOAD_FILE = gql` 5 5 mutation UploadFile($file: Upload!) { ··· 16 16 const { data, fetching, error } = result; 17 17 18 18 const handleFileUpload = () => { 19 - uploadFile({ file: selectedFile}) 20 - } 19 + uploadFile({ file: selectedFile }); 20 + }; 21 21 22 - const handleFileChange = (event) => { 22 + const handleFileChange = event => { 23 23 setSelectedFile(event.target.files[0]); 24 - } 24 + }; 25 25 26 26 return ( 27 27 <div> ··· 31 31 32 32 {data && data.uploadFile ? ( 33 33 <p>File uploaded to {data.uploadFile.filename}</p> 34 - ): ( 34 + ) : ( 35 35 <div> 36 36 <input type="file" onChange={handleFileChange} /> 37 37 38 - <button onClick={handleFileUpload}> 39 - Upload! 40 - </button> 38 + <button onClick={handleFileUpload}>Upload!</button> 41 39 </div> 42 40 )} 43 41 </div>
+7
examples/with-multipart/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+2 -2
examples/with-next/pages/_app.js
··· 1 - import { withUrqlClient } from "next-urql"; 1 + import { withUrqlClient } from 'next-urql'; 2 2 3 3 const App = ({ Component, pageProps }) => <Component {...pageProps} />; 4 4 5 5 export default withUrqlClient( 6 6 () => ({ 7 - url: "https://trygql.formidable.dev/graphql/basic-pokedex" 7 + url: 'https://trygql.formidable.dev/graphql/basic-pokedex', 8 8 }), 9 9 { ssr: false } 10 10 )(App);
+4 -7
examples/with-next/pages/index.js
··· 1 - import { withUrqlClient } from "next-urql"; 2 - import { 3 - useQuery, 4 - gql 5 - } from "urql"; 1 + import { withUrqlClient } from 'next-urql'; 2 + import { useQuery, gql } from 'urql'; 6 3 7 4 const POKEMONS_QUERY = gql` 8 5 query { ··· 19 16 return ( 20 17 <div> 21 18 <h1>Static</h1> 22 - {res.data.pokemons.map((pokemon) => ( 19 + {res.data.pokemons.map(pokemon => ( 23 20 <div key={pokemon.id}> 24 21 {pokemon.id} - {pokemon.name} 25 22 </div> ··· 30 27 31 28 export default withUrqlClient( 32 29 () => ({ 33 - url: "https://trygql.formidable.dev/graphql/basic-pokedex" 30 + url: 'https://trygql.formidable.dev/graphql/basic-pokedex', 34 31 }), 35 32 { ssr: true } 36 33 )(Index);
+12 -9
examples/with-next/pages/server.js
··· 1 - import { initUrqlClient } from "next-urql"; 1 + import { initUrqlClient } from 'next-urql'; 2 2 import { 3 3 ssrExchange, 4 4 dedupExchange, 5 5 cacheExchange, 6 6 fetchExchange, 7 7 useQuery, 8 - gql 9 - } from "urql"; 8 + gql, 9 + } from 'urql'; 10 10 11 11 const POKEMONS_QUERY = gql` 12 12 query { ··· 23 23 return ( 24 24 <div> 25 25 <h1>Server-side render</h1> 26 - {res.data.pokemons.map((pokemon) => ( 26 + {res.data.pokemons.map(pokemon => ( 27 27 <div key={pokemon.id}> 28 28 {pokemon.id} - {pokemon.name} 29 29 </div> ··· 34 34 35 35 export async function getServerSideProps() { 36 36 const ssrCache = ssrExchange({ isClient: false }); 37 - const client = initUrqlClient({ 38 - url: "https://trygql.formidable.dev/graphql/basic-pokedex", 39 - exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange] 40 - }, false); 37 + const client = initUrqlClient( 38 + { 39 + url: 'https://trygql.formidable.dev/graphql/basic-pokedex', 40 + exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange], 41 + }, 42 + false 43 + ); 41 44 42 45 // This query is used to populate the cache for the query 43 46 // used on this page. ··· 46 49 return { 47 50 props: { 48 51 // urqlState is a keyword here so withUrqlClient can pick it up. 49 - urqlState: ssrCache.extractData() 52 + urqlState: ssrCache.extractData(), 50 53 }, 51 54 }; 52 55 }
+12 -9
examples/with-next/pages/static.js
··· 1 - import { initUrqlClient } from "next-urql"; 1 + import { initUrqlClient } from 'next-urql'; 2 2 import { 3 3 ssrExchange, 4 4 dedupExchange, 5 5 cacheExchange, 6 6 fetchExchange, 7 7 useQuery, 8 - gql 9 - } from "urql"; 8 + gql, 9 + } from 'urql'; 10 10 11 11 const POKEMONS_QUERY = gql` 12 12 query { ··· 23 23 return ( 24 24 <div> 25 25 <h1>Static</h1> 26 - {res.data.pokemons.map((pokemon) => ( 26 + {res.data.pokemons.map(pokemon => ( 27 27 <div key={pokemon.id}> 28 28 {pokemon.id} - {pokemon.name} 29 29 </div> ··· 34 34 35 35 export async function getStaticProps() { 36 36 const ssrCache = ssrExchange({ isClient: false }); 37 - const client = initUrqlClient({ 38 - url: "https://trygql.formidable.dev/graphql/basic-pokedex", 39 - exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange] 40 - }, false); 37 + const client = initUrqlClient( 38 + { 39 + url: 'https://trygql.formidable.dev/graphql/basic-pokedex', 40 + exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange], 41 + }, 42 + false 43 + ); 41 44 42 45 // This query is used to populate the cache for the query 43 46 // used on this page. ··· 46 49 return { 47 50 props: { 48 51 // urqlState is a keyword here so withUrqlClient can pick it up. 49 - urqlState: ssrCache.extractData() 52 + urqlState: ssrCache.extractData(), 50 53 }, 51 54 }; 52 55 }
+3 -3
examples/with-pagination/README.md
··· 16 16 17 17 This example contains: 18 18 19 - - The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.js) 20 - - A managing component called `PaginatedNpmSearch` set up to render all pages in [`src/PaginatedNpmSearch.js`](src/PaginatedNpmSearch.js) 21 - - A page component called `SearchResultPage` running page queries in [`src/PaginatedNpmSearch.js`](src/PaginatedNpmSearch.js) 19 + - The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.jsx) 20 + - A managing component called `PaginatedNpmSearch` set up to render all pages in [`src/PaginatedNpmSearch.jss`](src/PaginatedNpmSearch.jsx) 21 + - A page component called `SearchResultPage` running page queries in [`src/PaginatedNpmSearch.jsx`](src/PaginatedNpmSearch.jsx)
+27
examples/with-pagination/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-pagination</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-pagination/package.json
··· 2 2 "name": "with-pagination", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "graphql": "^15.5.0", 7 10 "react": "^17.0.2", ··· 9 12 "urql": "^2.0.2" 10 13 }, 11 14 "devDependencies": { 12 - "react-scripts": "4.0.3" 13 - }, 14 - "scripts": { 15 - "start": "react-scripts start", 16 - "build": "react-scripts build", 17 - "test": "react-scripts test", 18 - "eject": "react-scripts eject" 19 - }, 20 - "browserslist": { 21 - "production": [ 22 - ">0.2%", 23 - "not dead", 24 - "not op_mini all" 25 - ], 26 - "development": [ 27 - "last 1 chrome version", 28 - "last 1 firefox version", 29 - "last 1 safari version" 30 - ] 15 + "@vitejs/plugin-react-refresh": "^1.3.3", 16 + "vite": "^2.2.4" 31 17 } 32 18 }
-17
examples/with-pagination/src/App.js
··· 1 - import { createClient, Provider } from "urql"; 2 - 3 - import PaginatedNpmSearch from "./pages/PaginatedNpmSearch"; 4 - 5 - const client = createClient({ 6 - url: "https://trygql.formidable.dev/graphql/relay-npm", 7 - }); 8 - 9 - function App() { 10 - return ( 11 - <Provider value={client}> 12 - <PaginatedNpmSearch /> 13 - </Provider> 14 - ); 15 - } 16 - 17 - export default App;
+18
examples/with-pagination/src/App.jsx
··· 1 + import React from 'react'; 2 + import { createClient, Provider } from 'urql'; 3 + 4 + import PaginatedNpmSearch from './PaginatedNpmSearch'; 5 + 6 + const client = createClient({ 7 + url: 'https://trygql.formidable.dev/graphql/relay-npm', 8 + }); 9 + 10 + function App() { 11 + return ( 12 + <Provider value={client}> 13 + <PaginatedNpmSearch /> 14 + </Provider> 15 + ); 16 + } 17 + 18 + export default App;
-13
examples/with-pagination/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-pagination/src/index.js examples/with-react/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
+19 -15
examples/with-pagination/src/pages/PaginatedNpmSearch.js examples/with-pagination/src/PaginatedNpmSearch.jsx
··· 1 - import React, { useState } from "react"; 2 - import { gql, useQuery } from "urql"; 1 + import React, { useState } from 'react'; 2 + import { gql, useQuery } from 'urql'; 3 3 4 4 const limit = 5; 5 - const query = "graphql"; 5 + const query = 'graphql'; 6 6 7 7 const NPM_SEARCH = gql` 8 8 query Search($query: String!, $first: Int!, $after: String) { ··· 19 19 } 20 20 `; 21 21 22 - const SearchResultPage = ({ variables, onLoadMore, isLastPage }) => { 22 + const SearchResultPage = ({ variables, onLoadMore, isLastPage }) => { 23 23 const [result] = useQuery({ query: NPM_SEARCH, variables }); 24 24 25 25 const { data, fetching, error } = result; ··· 34 34 35 35 {searchResults && ( 36 36 <> 37 - {searchResults.nodes.map((packageInfo) => ( 38 - <div key={packageInfo.id}>{packageInfo.id}: {packageInfo.name}</div> 37 + {searchResults.nodes.map(packageInfo => ( 38 + <div key={packageInfo.id}> 39 + {packageInfo.id}: {packageInfo.name} 40 + </div> 39 41 ))} 40 42 41 43 {isLastPage && searchResults.pageInfo.hasNextPage && ( 42 - <button onClick={() => onLoadMore(searchResults.pageInfo.endCursor)}> 44 + <button 45 + onClick={() => onLoadMore(searchResults.pageInfo.endCursor)} 46 + > 43 47 load more 44 48 </button> 45 49 )} ··· 51 55 52 56 const PaginatedNpmSearch = () => { 53 57 const [pageVariables, setPageVariables] = useState([ 54 - { 55 - query, 56 - first: limit, 57 - after: "" 58 - }, 59 - ]); 58 + { 59 + query, 60 + first: limit, 61 + after: '', 62 + }, 63 + ]); 60 64 61 65 return ( 62 66 <div> 63 67 {pageVariables.map((variables, i) => ( 64 68 <SearchResultPage 65 - key={"" + variables.after} 69 + key={'' + variables.after} 66 70 variables={variables} 67 71 isLastPage={i === pageVariables.length - 1} 68 - onLoadMore={(after) => 72 + onLoadMore={after => 69 73 setPageVariables([...pageVariables, { after, first: limit, query }]) 70 74 } 71 75 />
+7
examples/with-pagination/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+2 -2
examples/with-react/README.md
··· 15 15 16 16 This example contains: 17 17 18 - - The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.js) 19 - - A query for pokémon in [`src/pages/PokemonList.js`](src/pages/PokemonList.js) 18 + - The `urql` bindings and a React app with a client set up in [`src/App.jsx`](src/App.jsx) 19 + - A query for pokémon in [`src/PokemonList.jsx`](src/PokemonList.jsx)
+27
examples/with-react/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-react</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-react/package.json
··· 2 2 "name": "with-react", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "graphql": "^15.5.0", 7 10 "react": "^17.0.2", ··· 9 12 "urql": "^2.0.2" 10 13 }, 11 14 "devDependencies": { 12 - "react-scripts": "4.0.3" 13 - }, 14 - "scripts": { 15 - "start": "react-scripts start", 16 - "build": "react-scripts build", 17 - "test": "react-scripts test", 18 - "eject": "react-scripts eject" 19 - }, 20 - "browserslist": { 21 - "production": [ 22 - ">0.2%", 23 - "not dead", 24 - "not op_mini all" 25 - ], 26 - "development": [ 27 - "last 1 chrome version", 28 - "last 1 firefox version", 29 - "last 1 safari version" 30 - ] 15 + "@vitejs/plugin-react-refresh": "^1.3.3", 16 + "vite": "^2.2.4" 31 17 } 32 18 }
-17
examples/with-react/src/App.js
··· 1 - import { createClient, Provider } from "urql"; 2 - 3 - import PokemonList from "./pages/PokemonList"; 4 - 5 - const client = createClient({ 6 - url: "https://trygql.formidable.dev/graphql/basic-pokedex", 7 - }); 8 - 9 - function App() { 10 - return ( 11 - <Provider value={client}> 12 - <PokemonList /> 13 - </Provider> 14 - ); 15 - } 16 - 17 - export default App;
+18
examples/with-react/src/App.jsx
··· 1 + import React from 'react'; 2 + import { createClient, Provider } from 'urql'; 3 + 4 + import PokemonList from './PokemonList'; 5 + 6 + const client = createClient({ 7 + url: 'https://trygql.formidable.dev/graphql/basic-pokedex', 8 + }); 9 + 10 + function App() { 11 + return ( 12 + <Provider value={client}> 13 + <PokemonList /> 14 + </Provider> 15 + ); 16 + } 17 + 18 + export default App;
-13
examples/with-react/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-react/src/index.js examples/with-refresh-auth/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
+3 -3
examples/with-react/src/pages/PokemonList.js examples/with-react/src/PokemonList.jsx
··· 1 - import React from "react"; 2 - import { gql, useQuery } from "urql"; 1 + import React from 'react'; 2 + import { gql, useQuery } from 'urql'; 3 3 4 4 const POKEMONS_QUERY = gql` 5 5 query Pokemons { ··· 23 23 24 24 {data && ( 25 25 <ul> 26 - {data.pokemons.map((pokemon) => ( 26 + {data.pokemons.map(pokemon => ( 27 27 <li key={pokemon.id}>{pokemon.name}</li> 28 28 ))} 29 29 </ul>
+7
examples/with-react/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+5 -5
examples/with-refresh-auth/README.md
··· 17 17 18 18 This example contains: 19 19 20 - - The `urql` bindings and a React app set up in [`src/App.js`](src/App.js) 21 - - Some authentication glue code to store the tokens in [`src/auth/Store.js`](src/auth/Store.js) 22 - - The `Client` and the `authExchange` from `@urql/exchange-auth` set up in [`src/client/index.js`](src/client/index.js) 23 - - A basic login form in [`src/pages/LoginForm.js`](src/pages/LoginForm.js) 24 - - And a basic login guard on [`src/pages/Home.js`](src/pages/Home.js) 20 + - The `urql` bindings and a React app set up in [`src/App.jsx`](src/App.jsx) 21 + - Some authentication glue code to store the tokens in [`src/authStore.js`](src/authStore.js) 22 + - The `Client` and the `authExchange` from `@urql/exchange-auth` set up in [`src/client.js`](src/client.js) 23 + - A basic login form in [`src/pages/LoginForm.jsx`](src/pages/LoginForm.jsx) 24 + - And a basic login guard on [`src/App.jsx`](src/App.jsx) 25 25 (Note: This isn't using a query in this particular component, since this is just an example)
+27
examples/with-refresh-auth/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-refresh-auth</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-refresh-auth/package.json
··· 2 2 "name": "with-refresh-auth", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "@urql/exchange-auth": "^0.1.2", 7 10 "graphql": "^15.5.0", ··· 10 13 "urql": "^2.0.2" 11 14 }, 12 15 "devDependencies": { 13 - "react-scripts": "4.0.3" 14 - }, 15 - "scripts": { 16 - "start": "react-scripts start", 17 - "build": "react-scripts build", 18 - "test": "react-scripts test", 19 - "eject": "react-scripts eject" 20 - }, 21 - "browserslist": { 22 - "production": [ 23 - ">0.2%", 24 - "not dead", 25 - "not op_mini all" 26 - ], 27 - "development": [ 28 - "last 1 chrome version", 29 - "last 1 firefox version", 30 - "last 1 safari version" 31 - ] 16 + "@vitejs/plugin-react-refresh": "^1.3.3", 17 + "vite": "^2.2.4" 32 18 } 33 19 }
-15
examples/with-refresh-auth/src/App.js
··· 1 - import React from 'react'; 2 - import { Provider } from 'urql'; 3 - 4 - import client from './client'; 5 - import Home from './pages/Home'; 6 - 7 - function App() { 8 - return ( 9 - <Provider value={client}> 10 - <Home /> 11 - </Provider> 12 - ); 13 - } 14 - 15 - export default App;
+39
examples/with-refresh-auth/src/App.jsx
··· 1 + import React from 'react'; 2 + import { Provider } from 'urql'; 3 + 4 + import client from './client'; 5 + 6 + import { getToken, saveAuthData } from './authStore'; 7 + import Profile from './pages/Profile'; 8 + import LoginForm from './pages/LoginForm'; 9 + 10 + const Home = () => { 11 + const [isLoggedIn, setIsLoggedIn] = useState(false); 12 + 13 + const onLoginSuccess = auth => { 14 + saveAuthData(auth); 15 + setIsLoggedIn(true); 16 + }; 17 + 18 + useEffect(() => { 19 + if (getToken()) { 20 + setIsLoggedIn(true); 21 + } 22 + }, []); 23 + 24 + return isLoggedIn ? ( 25 + <Profile /> 26 + ) : ( 27 + <LoginForm onLoginSuccess={onLoginSuccess} /> 28 + ); 29 + }; 30 + 31 + function App() { 32 + return ( 33 + <Provider value={client}> 34 + <Home /> 35 + </Provider> 36 + ); 37 + } 38 + 39 + export default App;
examples/with-refresh-auth/src/auth/Store.js examples/with-refresh-auth/src/authStore.js
+116
examples/with-refresh-auth/src/client.js
··· 1 + import { 2 + makeOperation, 3 + createClient, 4 + dedupExchange, 5 + fetchExchange, 6 + cacheExchange, 7 + gql, 8 + } from 'urql'; 9 + 10 + import { authExchange } from '@urql/exchange-auth'; 11 + 12 + import { 13 + getRefreshToken, 14 + getToken, 15 + saveAuthData, 16 + clearStorage, 17 + } from './authStore'; 18 + 19 + const REFRESH_TOKEN_MUTATION = gql` 20 + mutation RefreshCredentials($refreshToken: String!) { 21 + refreshCredentials(refreshToken: $refreshToken) { 22 + refreshToken 23 + token 24 + } 25 + } 26 + `; 27 + 28 + const client = createClient({ 29 + url: 'https://trygql.formidable.dev/graphql/web-collections', 30 + exchanges: [ 31 + dedupExchange, 32 + cacheExchange, 33 + authExchange({ 34 + async getAuth({ authState, mutate }) { 35 + if (!authState) { 36 + const token = getToken(); 37 + const refreshToken = getRefreshToken(); 38 + 39 + if (token && refreshToken) { 40 + return { token, refreshToken }; 41 + } 42 + 43 + return null; 44 + } 45 + 46 + const result = await mutate(REFRESH_TOKEN_MUTATION, { 47 + refreshToken: authState.refreshToken, 48 + }); 49 + 50 + if (result.data?.refreshCredentials) { 51 + saveAuthData(result.data.refreshCredentials); 52 + 53 + return result.data.refreshCredentials; 54 + } 55 + 56 + // This is where auth has gone wrong and we need to clean up and redirect to a login page 57 + clearStorage(); 58 + window.location.reload(); 59 + 60 + return null; 61 + }, 62 + 63 + addAuthToOperation({ authState, operation }) { 64 + if (!authState || !authState.token) { 65 + return operation; 66 + } 67 + 68 + const fetchOptions = 69 + typeof operation.context.fetchOptions === 'function' 70 + ? operation.context.fetchOptions() 71 + : operation.context.fetchOptions || {}; 72 + 73 + return makeOperation(operation.kind, operation, { 74 + ...operation.context, 75 + fetchOptions: { 76 + ...fetchOptions, 77 + headers: { 78 + ...fetchOptions.headers, 79 + Authorization: `Bearer ${authState.token}`, 80 + }, 81 + }, 82 + }); 83 + }, 84 + 85 + didAuthError({ error }) { 86 + return error.graphQLErrors.some( 87 + e => e.extensions?.code === 'UNAUTHORIZED' 88 + ); 89 + }, 90 + 91 + willAuthError({ operation, authState }) { 92 + if (!authState) { 93 + // Detect our login mutation and let this operation through: 94 + return ( 95 + operation.kind !== 'mutation' || 96 + // Here we find any mutation definition with the "signin" field 97 + !operation.query.definitions.some(definition => { 98 + return ( 99 + definition.kind === 'OperationDefinition' && 100 + definition.selectionSet.selections.some(node => { 101 + // The field name is just an example, since register may also be an exception 102 + return node.kind === 'Field' && node.name.value === 'signin'; 103 + }) 104 + ); 105 + }) 106 + ); 107 + } 108 + 109 + return false; 110 + }, 111 + }), 112 + fetchExchange, 113 + ], 114 + }); 115 + 116 + export default client;
-112
examples/with-refresh-auth/src/client/index.js
··· 1 - import { 2 - createClient, 3 - dedupExchange, 4 - fetchExchange, 5 - cacheExchange, 6 - gql, 7 - } from 'urql'; 8 - import { makeOperation } from '@urql/core'; 9 - import { authExchange } from '@urql/exchange-auth'; 10 - import { 11 - getRefreshToken, 12 - getToken, 13 - saveAuthData, 14 - clearStorage, 15 - } from '../auth/Store'; 16 - 17 - const REFRESH_TOKEN_MUTATION = gql` 18 - mutation RefreshCredentials($refreshToken: String!) { 19 - refreshCredentials(refreshToken: $refreshToken) { 20 - refreshToken 21 - token 22 - } 23 - } 24 - `; 25 - 26 - const getAuth = async ({ authState, mutate }) => { 27 - if (!authState) { 28 - const token = getToken(); 29 - const refreshToken = getRefreshToken(); 30 - 31 - if (token && refreshToken) { 32 - return { token, refreshToken }; 33 - } 34 - 35 - return null; 36 - } 37 - 38 - const result = await mutate(REFRESH_TOKEN_MUTATION, { 39 - refreshToken: authState.refreshToken, 40 - }); 41 - 42 - if (result.data?.refreshCredentials) { 43 - saveAuthData(result.data.refreshCredentials); 44 - 45 - return result.data.refreshCredentials; 46 - } 47 - 48 - // This is where auth has gone wrong and we need to clean up and redirect to a login page 49 - clearStorage(); 50 - window.location.reload(); 51 - 52 - return null; 53 - }; 54 - 55 - const addAuthToOperation = ({ authState, operation }) => { 56 - if (!authState || !authState.token) { 57 - return operation; 58 - } 59 - 60 - const fetchOptions = 61 - typeof operation.context.fetchOptions === 'function' 62 - ? operation.context.fetchOptions() 63 - : operation.context.fetchOptions || {}; 64 - 65 - return makeOperation(operation.kind, operation, { 66 - ...operation.context, 67 - fetchOptions: { 68 - ...fetchOptions, 69 - headers: { 70 - ...fetchOptions.headers, 71 - Authorization: `Bearer ${authState.token}`, 72 - }, 73 - }, 74 - }); 75 - }; 76 - 77 - const didAuthError = ({ error }) => { 78 - return error.graphQLErrors.some(e => e.extensions?.code === 'UNAUTHORIZED'); 79 - }; 80 - 81 - const willAuthError = ({ operation, authState }) => { 82 - if (!authState) { 83 - // Detect our login mutation and let this operation through: 84 - return ( 85 - operation.kind !== 'mutation' || 86 - // Here we find any mutation definition with the "signin" field 87 - !operation.query.definitions.some(definition => { 88 - return ( 89 - definition.kind === 'OperationDefinition' && 90 - definition.selectionSet.selections.some(node => { 91 - // The field name is just an example, since register may also be an exception 92 - return node.kind === 'Field' && node.name.value === 'signin'; 93 - }) 94 - ); 95 - }) 96 - ); 97 - } 98 - 99 - return false; 100 - }; 101 - 102 - const client = createClient({ 103 - url: 'https://trygql.formidable.dev/graphql/web-collections', 104 - exchanges: [ 105 - dedupExchange, 106 - cacheExchange, 107 - authExchange({ getAuth, addAuthToOperation, didAuthError, willAuthError }), 108 - fetchExchange, 109 - ], 110 - }); 111 - 112 - export default client;
-13
examples/with-refresh-auth/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -4
examples/with-refresh-auth/src/index.js examples/with-retry/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>, 10 10 document.getElementById('root') 11 11 ); 12 -
-28
examples/with-refresh-auth/src/pages/Home.js
··· 1 - import React, { useEffect, useState } from 'react'; 2 - 3 - import { getToken, saveAuthData } from '../auth/Store'; 4 - import Profile from './Profile'; 5 - import LoginForm from './LoginForm'; 6 - 7 - const Home = () => { 8 - const [isLoggedIn, setIsLoggedIn] = useState(false); 9 - 10 - const onLoginSuccess = auth => { 11 - saveAuthData(auth); 12 - setIsLoggedIn(true); 13 - }; 14 - 15 - useEffect(() => { 16 - if (getToken()) { 17 - setIsLoggedIn(true); 18 - } 19 - }, []); 20 - 21 - return isLoggedIn ? ( 22 - <Profile /> 23 - ) : ( 24 - <LoginForm onLoginSuccess={onLoginSuccess} /> 25 - ); 26 - }; 27 - 28 - export default Home;
-57
examples/with-refresh-auth/src/pages/LoginForm.js
··· 1 - import React, { useEffect, useState } from 'react'; 2 - import { gql, useMutation } from 'urql'; 3 - 4 - const LOGIN_MUTATION = gql` 5 - mutation Signin($input: LoginInput!) { 6 - signin(input: $input) { 7 - refreshToken 8 - token 9 - } 10 - } 11 - `; 12 - 13 - const LoginForm = ({ onLoginSuccess }) => { 14 - const [password, setPassword] = useState(''); 15 - const [username, setUsername] = useState(''); 16 - 17 - const [loginResult, login] = useMutation(LOGIN_MUTATION); 18 - 19 - const { data, fetching, error } = loginResult; 20 - 21 - const onUsernameChange = evt => { 22 - setUsername(evt.target.value); 23 - }; 24 - 25 - const onPasswordChange = evt => { 26 - setPassword(evt.target.value); 27 - }; 28 - 29 - const onSubmit = evt => { 30 - evt.preventDefault(); 31 - login({ input: { username, password } }); 32 - }; 33 - 34 - useEffect(() => { 35 - if (data && data.signin) { 36 - onLoginSuccess(data.signin); 37 - } 38 - }, [onLoginSuccess, data]); 39 - 40 - if (fetching) { 41 - return <p>loading...</p>; 42 - } 43 - 44 - return ( 45 - <form onSubmit={onSubmit}> 46 - {error && <p>Oh no... {error.message}</p>} 47 - 48 - <input type="text" onChange={onUsernameChange} /> 49 - 50 - <input type="password" onChange={onPasswordChange} /> 51 - 52 - <input type="submit" title="login" /> 53 - </form> 54 - ); 55 - }; 56 - 57 - export default LoginForm;
+101
examples/with-refresh-auth/src/pages/LoginForm.jsx
··· 1 + import React from 'react'; 2 + import { gql, useMutation } from 'urql'; 3 + 4 + const LOGIN_MUTATION = gql` 5 + mutation Login($input: LoginInput!) { 6 + signin(input: $input) { 7 + refreshToken 8 + token 9 + } 10 + } 11 + `; 12 + 13 + const REGISTER_MUTATION = gql` 14 + mutation Register($input: LoginInput!) { 15 + register(input: $input) { 16 + refreshToken 17 + token 18 + } 19 + } 20 + `; 21 + 22 + const LoginForm = ({ onLoginSuccess }) => { 23 + const [loginResult, login] = useMutation(LOGIN_MUTATION); 24 + const [registerResult, register] = useMutation(REGISTER_MUTATION); 25 + 26 + const onSubmitLogin = event => { 27 + event.preventDefault(); 28 + const data = new FormData(event.target); 29 + const username = data.get('username'); 30 + const password = data.get('password'); 31 + 32 + login({ input: { username, password } }).then(result => { 33 + if (!result.error && result.data && result.data.signin) { 34 + onLoginSuccess(result.data.signin); 35 + } 36 + }); 37 + }; 38 + 39 + const onSubmitRegister = event => { 40 + event.preventDefault(); 41 + const data = new FormData(event.target); 42 + const username = data.get('username'); 43 + const password = data.get('password'); 44 + 45 + register({ input: { username, password } }).then(result => { 46 + if (!result.error && result.data && result.data.register) { 47 + onLoginSuccess(result.data.register); 48 + } 49 + }); 50 + }; 51 + 52 + const disabled = loginResult.fetching || registerResult.fetching; 53 + 54 + return ( 55 + <> 56 + <form onSubmit={onSubmitLogin}> 57 + {loginResult.fetching ? <p>Logging in...</p> : null} 58 + {loginResult.error ? <p>Oh no... {loginResult.error.message}</p> : null} 59 + 60 + <fieldset disabled={disabled ? 'disabled' : null}> 61 + <h3>Login</h3> 62 + <label> 63 + Username: 64 + <input name="username" type="text" /> 65 + </label> 66 + 67 + <label> 68 + Password: 69 + <input name="password" type="password" /> 70 + </label> 71 + 72 + <button type="submit">Login</button> 73 + </fieldset> 74 + </form> 75 + 76 + <form onSubmit={onSubmitRegister}> 77 + {registerResult.fetching ? <p>Signing up...</p> : null} 78 + {registerResult.error ? ( 79 + <p>Oh no... {registerResult.error.message}</p> 80 + ) : null} 81 + 82 + <fieldset disabled={disabled ? 'disabled' : null}> 83 + <h3>Register</h3> 84 + <label> 85 + {'Username: '} 86 + <input name="username" type="text" /> 87 + </label> 88 + 89 + <label> 90 + {'Password: '} 91 + <input name="password" type="password" /> 92 + </label> 93 + 94 + <button type="submit">Register</button> 95 + </fieldset> 96 + </form> 97 + </> 98 + ); 99 + }; 100 + 101 + export default LoginForm;
examples/with-refresh-auth/src/pages/Profile.js examples/with-refresh-auth/src/pages/Profile.jsx
+7
examples/with-refresh-auth/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+3 -3
examples/with-retry/README.md
··· 42 42 43 43 This example contains: 44 44 45 - - The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.js) 46 - - The `retryExchange` from `@urql/exchange-retry` in [`src/App.js`](src/App.js) 47 - - A random colour query in [`src/pages/Color.js`](src/pages/Color.js) 45 + - The `urql` bindings and a React app with a client set up in [`src/App.jsx`](src/App.jsx) 46 + - The `retryExchange` from `@urql/exchange-retry` in [`src/App.jsx`](src/App.jsx) 47 + - A random colour query in [`src/Color.jsx`](src/pages/Color.jsx)
+27
examples/with-retry/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>with-retry</title> 7 + <style> 8 + body { 9 + margin: 0; 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 + sans-serif; 13 + -webkit-font-smoothing: antialiased; 14 + -moz-osx-font-smoothing: grayscale; 15 + } 16 + 17 + code { 18 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 + monospace; 20 + } 21 + </style> 22 + </head> 23 + <body> 24 + <div id="root"></div> 25 + <script type="module" src="/src/index.jsx"></script> 26 + </body> 27 + </html>
+5 -19
examples/with-retry/package.json
··· 2 2 "name": "with-retry", 3 3 "version": "0.0.0", 4 4 "private": true, 5 + "scripts": { 6 + "start": "vite" 7 + }, 5 8 "dependencies": { 6 9 "@urql/exchange-retry": "^0.2.0", 7 10 "graphql": "^15.5.0", ··· 10 13 "urql": "^2.0.2" 11 14 }, 12 15 "devDependencies": { 13 - "react-scripts": "4.0.3" 14 - }, 15 - "scripts": { 16 - "start": "react-scripts start", 17 - "build": "react-scripts build", 18 - "test": "react-scripts test", 19 - "eject": "react-scripts eject" 20 - }, 21 - "browserslist": { 22 - "production": [ 23 - ">0.2%", 24 - "not dead", 25 - "not op_mini all" 26 - ], 27 - "development": [ 28 - "last 1 chrome version", 29 - "last 1 firefox version", 30 - "last 1 safari version" 31 - ] 16 + "@vitejs/plugin-react-refresh": "^1.3.3", 17 + "vite": "^2.2.4" 32 18 } 33 19 }
-29
examples/with-retry/src/App.js
··· 1 - import { createClient, fetchExchange, Provider } from "urql"; 2 - import { retryExchange } from '@urql/exchange-retry'; 3 - 4 - import Color from "./pages/Color"; 5 - 6 - const client = createClient({ 7 - url: "https://trygql.formidable.dev/graphql/intermittent-colors", 8 - exchanges: [ 9 - retryExchange({ 10 - maxNumberAttempts: 5, 11 - retryIf: error => { 12 - // NOTE: With this deemo schema we have a specific random error to look out for: 13 - return error.graphQLErrors.some(x => x.extensions?.code === 'NO_SOUP') 14 - || !!error.networkError; 15 - } 16 - }), 17 - fetchExchange 18 - ], 19 - }); 20 - 21 - function App() { 22 - return ( 23 - <Provider value={client}> 24 - <Color /> 25 - </Provider> 26 - ); 27 - } 28 - 29 - export default App;
+32
examples/with-retry/src/App.jsx
··· 1 + import React from 'react'; 2 + import { createClient, fetchExchange, Provider } from 'urql'; 3 + import { retryExchange } from '@urql/exchange-retry'; 4 + 5 + import Color from './Color'; 6 + 7 + const client = createClient({ 8 + url: 'https://trygql.formidable.dev/graphql/intermittent-colors', 9 + exchanges: [ 10 + retryExchange({ 11 + maxNumberAttempts: 5, 12 + retryIf: error => { 13 + // NOTE: With this deemo schema we have a specific random error to look out for: 14 + return ( 15 + error.graphQLErrors.some(x => x.extensions?.code === 'NO_SOUP') || 16 + !!error.networkError 17 + ); 18 + }, 19 + }), 20 + fetchExchange, 21 + ], 22 + }); 23 + 24 + function App() { 25 + return ( 26 + <Provider value={client}> 27 + <Color /> 28 + </Provider> 29 + ); 30 + } 31 + 32 + export default App;
-13
examples/with-retry/src/index.css
··· 1 - body { 2 - margin: 0; 3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 - sans-serif; 6 - -webkit-font-smoothing: antialiased; 7 - -moz-osx-font-smoothing: grayscale; 8 - } 9 - 10 - code { 11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 - monospace; 13 - }
+3 -3
examples/with-retry/src/index.js examples/with-apq/src/index.jsx
··· 1 1 import React from 'react'; 2 - import ReactDOM from 'react-dom'; 3 - import './index.css'; 2 + import { render } from 'react-dom'; 3 + 4 4 import App from './App'; 5 5 6 - ReactDOM.render( 6 + render( 7 7 <React.StrictMode> 8 8 <App /> 9 9 </React.StrictMode>,
+7 -4
examples/with-retry/src/pages/Color.js examples/with-retry/src/Color.jsx
··· 1 - import React from "react"; 2 - import { gql, useQuery } from "urql"; 1 + import React from 'react'; 2 + import { gql, useQuery } from 'urql'; 3 3 4 4 const RANDOM_COLOR_QUERY = gql` 5 5 query RandomColor { ··· 26 26 {data.randomColor.name} 27 27 </div> 28 28 )} 29 - 29 + 30 30 {result.operation && ( 31 - <p>We retried {result.operation.context.retryCount} times to get a result without an error.</p> 31 + <p> 32 + We retried {result.operation.context.retryCount} times to get a result 33 + without an error. 34 + </p> 32 35 )} 33 36 </div> 34 37 );
+7
examples/with-retry/vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import reactRefresh from '@vitejs/plugin-react-refresh'; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [reactRefresh()], 7 + });
+1 -2
examples/with-svelte/index.html
··· 2 2 <html lang="en"> 3 3 <head> 4 4 <meta charset="UTF-8" /> 5 - <link rel="icon" href="/favicon.ico" /> 6 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 - <title>Svelte + Vite App</title> 6 + <title>with-svelte</title> 8 7 </head> 9 8 <body> 10 9 <div id="app"></div>
+2 -2
examples/with-svelte/src/main.js
··· 1 1 import App from './App.svelte'; 2 2 3 3 var app = new App({ 4 - target: document.body 4 + target: document.body, 5 5 }); 6 6 7 - export default app; 7 + export default app;
+4 -4
examples/with-svelte/vite.config.js
··· 1 - import { defineConfig } from 'vite' 2 - import svelte from '@sveltejs/vite-plugin-svelte' 1 + import { defineConfig } from 'vite'; 2 + import svelte from '@sveltejs/vite-plugin-svelte'; 3 3 4 4 // https://vitejs.dev/config/ 5 5 export default defineConfig({ 6 - plugins: [svelte()] 7 - }) 6 + plugins: [svelte()], 7 + });
-1
examples/with-vue3/index.html
··· 2 2 <html lang="en"> 3 3 <head> 4 4 <meta charset="UTF-8"> 5 - <link rel="icon" href="/favicon.ico" /> 6 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 6 <title>Vite App</title> 8 7 </head>
+3 -3
examples/with-vue3/src/main.js
··· 1 - import { createApp } from 'vue' 2 - import App from './App.vue' 1 + import { createApp } from 'vue'; 2 + import App from './App.vue'; 3 3 4 - createApp(App).mount('#app') 4 + createApp(App).mount('#app');
+4 -4
examples/with-vue3/vite.config.js
··· 1 - import { defineConfig } from 'vite' 2 - import vue from '@vitejs/plugin-vue' 1 + import { defineConfig } from 'vite'; 2 + import vue from '@vitejs/plugin-vue'; 3 3 4 4 // https://vitejs.dev/config/ 5 5 export default defineConfig({ 6 - plugins: [vue()] 7 - }) 6 + plugins: [vue()], 7 + });
+4 -1
package.json
··· 31 31 ], 32 32 "extends": [ 33 33 "./scripts/eslint/js-preset.js" 34 - ] 34 + ], 35 + "rules": { 36 + "import/no-unresolved": "off" 37 + } 35 38 } 36 39 ], 37 40 "eslintIgnore": [
+16 -2
scripts/eslint/common.js
··· 17 17 'build/', 18 18 'coverage/', 19 19 'benchmark/', 20 - 'example/', 21 - 'examples/', 22 20 'scripts/' 23 21 ], 24 22 plugins: [ ··· 76 74 settings: { 77 75 react: { 78 76 version: 'detect', 77 + }, 78 + 'import/extensions': [ 79 + '.js', 80 + '.jsx', 81 + '.ts', 82 + '.tsx', 83 + ], 84 + 'import/resolver': { 85 + node: { 86 + extensions: [ 87 + '.js', 88 + '.jsx', 89 + '.ts', 90 + '.tsx', 91 + ] 92 + }, 79 93 }, 80 94 }, 81 95 };