├── assets ├── icon.png ├── favicon.png ├── splash.png └── icon-ios.png ├── babel.config.js ├── .gitignore ├── .expo-shared └── assets.json ├── src ├── Loading.js ├── styles.js ├── HomeScreen.js └── ChapterScreen.js ├── CHANGELOG.md ├── app.json ├── package.json ├── README.md └── App.js /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GraphQLGuide/guide-react-native/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GraphQLGuide/guide-react-native/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GraphQLGuide/guide-react-native/HEAD/assets/splash.png -------------------------------------------------------------------------------- /assets/icon-ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GraphQLGuide/guide-react-native/HEAD/assets/icon-ios.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true) 3 | return { 4 | presets: ['babel-preset-expo'], 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "b16ca03b8ec79df78b00abe3deb7f10948ab7195933127afefd6a94bc516e36b": true, 3 | "b39052b09ee4b3f28ec0461d66b0b8b2f439d3f49112234912ac9d18ae9e0647": true, 4 | "7b1e2274ea18550cf3628c4b375960e859206bce3f70ca15c368638c3d4982c0": true 5 | } 6 | -------------------------------------------------------------------------------- /src/Loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, ActivityIndicator } from 'react-native' 3 | 4 | import styles, { PINK } from './styles' 5 | 6 | export default () => ( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | export const PINK = '#ff5dc8' 4 | 5 | export const screenOptions = { 6 | headerStyle: { 7 | backgroundColor: PINK, 8 | }, 9 | headerTintColor: '#fff', 10 | } 11 | 12 | export default StyleSheet.create({ 13 | centered: { 14 | flex: 1, 15 | justifyContent: 'center', 16 | alignItems: 'center', 17 | }, 18 | item: { 19 | paddingTop: 16, 20 | paddingBottom: 16, 21 | paddingLeft: 20, 22 | paddingRight: 20, 23 | borderBottomWidth: 1, 24 | borderBottomColor: '#cccccc', 25 | }, 26 | header: { 27 | fontWeight: 'bold', 28 | }, 29 | subheader: { 30 | paddingTop: 10, 31 | }, 32 | }) 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes will be documented in this file. 3 | 4 | This file applies to the app that is created in Chapter 8 of the book—so the numbered step branches, not `master`. 5 | 6 | Releases are done with tags in the form `[step]_[version]`, so for instance step `3` of version `1.0.0` is `3_1.0.0`, located here: 7 | 8 | [/GraphQLGuide/guide-android/releases/tag/3_1.0.0](https://github.com/GraphQLGuide/guide-android/releases/tag/3_1.0.0) 9 | 10 | 15 | 16 | ## [1.0.0] - 2021-04-06 17 | Released with [`r6`](https://github.com/GraphQLGuide/book/releases/tag/r6) 18 | 19 | ### Added 20 | - Branches `0-3` 21 | 22 | ``` 23 | @apollo/client 3.2.1 24 | expo 39.0.2 25 | react 16.13.1 26 | ``` 27 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "guide", 4 | "slug": "guide", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "primaryColor": "#ff5dc8", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "cover", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "updates": { 15 | "fallbackToCacheTimeout": 0 16 | }, 17 | "assetBundlePatterns": ["**/*"], 18 | "ios": { 19 | "bundleIdentifier": "guide.graphql.guide", 20 | "buildNumber": "1.0.0", 21 | "supportsTablet": true, 22 | "icon": "./assets/icon-ios.png" 23 | }, 24 | "android": { 25 | "package": "guide.graphql.guide", 26 | "versionCode": 1, 27 | "permissions": [] 28 | }, 29 | "web": { 30 | "favicon": "./assets/favicon.png" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@apollo/client": "^3.2.1", 12 | "@react-native-community/async-storage": "^1.12.0", 13 | "@react-native-community/masked-view": "0.1.10", 14 | "@react-navigation/native": "^5.7.4", 15 | "@react-navigation/stack": "^5.9.1", 16 | "apollo3-cache-persist": "^0.6.0", 17 | "expo": "~39.0.2", 18 | "expo-status-bar": "~1.0.2", 19 | "graphql": "^15.3.0", 20 | "react": "16.13.1", 21 | "react-dom": "16.13.1", 22 | "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.2.tar.gz", 23 | "react-native-gesture-handler": "~1.7.0", 24 | "react-native-reanimated": "~1.13.0", 25 | "react-native-safe-area-context": "3.1.4", 26 | "react-native-screens": "~2.10.1", 27 | "react-native-web": "~0.13.12" 28 | }, 29 | "devDependencies": { 30 | "@babel/core": "~7.9.0" 31 | }, 32 | "private": true 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## The GraphQL Guide React Native app 2 | 3 | - Entry point: [App.js](https://github.com/GraphQLGuide/guide-react-native/blob/master/App.js) 4 | - Two screens, each with a GraphQL query: 5 | - [src/HomeScreen.js](https://github.com/GraphQLGuide/guide-react-native/blob/master/src/HomeScreen.js) 6 | - [src/ChapterScreen.js](https://github.com/GraphQLGuide/guide-react-native/blob/master/src/ChapterScreen.js) 7 | 8 | This example app is built, tutorial-style, in the React Native chapter of [The GraphQL Guide](https://graphql.guide/). Here are the tutorial steps: 9 | 10 | - [Starting branch: 0](https://github.com/GraphQLGuide/guide-react-native/tree/0) app skeleton 11 | - [Step 1: 0...1](https://github.com/GraphQLGuide/guide-react-native/compare/0...1) adding Apollo Client and first query 12 | - [Step 2: 1...2](https://github.com/GraphQLGuide/guide-react-native/compare/1...2) adding second screen and query 13 | - [Step 3: 2...3](https://github.com/GraphQLGuide/guide-react-native/compare/2...3) persisting the cache 14 | 15 | ### Running the app 16 | 17 | ```sh 18 | git clone https://github.com/GraphQLGuide/guide-react-native.git 19 | cd guide-react-native/ 20 | npm install 21 | npm start 22 | ``` 23 | 24 | This will start the Expo bundler, and you can open the app on Android, iOS, or web. 25 | 26 | ![App running on iOS device](https://res.cloudinary.com/graphql/image/upload/v1601574720/guide/expo-home-screen.png) 27 | -------------------------------------------------------------------------------- /src/HomeScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, FlatList, Pressable } from 'react-native' 3 | import { gql, useQuery } from '@apollo/client' 4 | import { AppLoading } from 'expo' 5 | 6 | import styles from './styles' 7 | 8 | const CHAPTERS_QUERY = gql` 9 | query Chapters { 10 | chapters { 11 | id 12 | number 13 | title 14 | } 15 | } 16 | ` 17 | 18 | const ChapterItem = ({ chapter, onPress }) => { 19 | const { number, title } = chapter 20 | let header, subheader 21 | 22 | if (number) { 23 | header = `Chapter ${number}` 24 | subheader = title 25 | } else { 26 | header = title 27 | } 28 | 29 | return ( 30 | 31 | {header} 32 | {subheader && {subheader}} 33 | 34 | ) 35 | } 36 | 37 | export default ({ navigation }) => { 38 | const { data, loading } = useQuery(CHAPTERS_QUERY) 39 | 40 | if (loading) { 41 | return 42 | } 43 | 44 | return ( 45 | ( 48 | navigation.navigate('Chapter', { chapter: item })} 51 | /> 52 | )} 53 | keyExtractor={(chapter) => chapter.id.toString()} 54 | /> 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /src/ChapterScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, FlatList } from 'react-native' 3 | import { gql, useQuery } from '@apollo/client' 4 | 5 | import styles from './styles' 6 | import Loading from './Loading' 7 | 8 | const SECTIONS_QUERY = gql` 9 | query Sections($id: Int!) { 10 | chapter(id: $id) { 11 | sections { 12 | number 13 | title 14 | } 15 | } 16 | } 17 | ` 18 | 19 | const SectionItem = ({ section, chapter }) => ( 20 | 21 | 22 | {chapter.number}.{section.number}: {section.title} 23 | 24 | 25 | ) 26 | 27 | export default ({ route }) => { 28 | const { data, loading } = useQuery(SECTIONS_QUERY, { 29 | variables: { id: route.params.chapter.id }, 30 | }) 31 | 32 | if (loading) { 33 | return 34 | } 35 | 36 | const { 37 | chapter: { sections }, 38 | } = data 39 | 40 | if (sections.length === 1) { 41 | return ( 42 | 43 | No sections 44 | 45 | ) 46 | } 47 | 48 | return ( 49 | ( 52 | 53 | )} 54 | keyExtractor={(section) => section.number.toString()} 55 | initialNumToRender={15} 56 | /> 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { StatusBar } from 'expo-status-bar' 3 | import { NavigationContainer } from '@react-navigation/native' 4 | import { createStackNavigator } from '@react-navigation/stack' 5 | import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client' 6 | import AsyncStorage from '@react-native-community/async-storage' 7 | import { persistCache } from 'apollo3-cache-persist' 8 | import { AppLoading } from 'expo' 9 | 10 | import HomeScreen from './src/HomeScreen' 11 | import ChapterScreen from './src/ChapterScreen' 12 | import { screenOptions } from './src/styles' 13 | 14 | const Stack = createStackNavigator() 15 | 16 | const cache = new InMemoryCache() 17 | 18 | const client = new ApolloClient({ 19 | uri: 'https://api.graphql.guide/graphql', 20 | cache, 21 | defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network' } }, 22 | }) 23 | 24 | export default function App() { 25 | const [loadingCache, setLoadingCache] = useState(true) 26 | 27 | useEffect(() => { 28 | persistCache({ 29 | cache, 30 | storage: AsyncStorage, 31 | }).then(() => setLoadingCache(false)) 32 | }, []) 33 | 34 | if (loadingCache) { 35 | return 36 | } 37 | 38 | return ( 39 | 40 | 41 | 42 | 47 | ({ 57 | title: number ? `Chapter ${number}: ${title}` : title, 58 | gestureResponseDistance: { horizontal: 500 }, 59 | })} 60 | /> 61 | 62 | 63 | 64 | 65 | ) 66 | } 67 | --------------------------------------------------------------------------------