翻译 |《JavaScript Everywhere》第 23 章 GraphQL 和 React Native
写在最后面
大家好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。
为了进步大家的浏览体验,对语句的构造和内容略有调整。如果发现本文中有存在瑕疵的中央,或者你有任何意见或者倡议,能够在评论区留言,或者加我的微信:code_maomao,欢送互相沟通交流学习。
(σ゚∀゚)σ..:*☆哎哟不错哦
第 23 章 GraphQL 和 React Native
在宾夕法尼亚州匹兹堡的安迪·沃霍尔博物馆,有一个名为“银云”的永恒安装。
该安装是一个稠密的房间,外面有十几个矩形的铝箔气球,每个气球都充斥了氦气和一般空气。后果是,气球的悬浮工夫要比充斥大气的气球更长,但不会像氦气球那样沉没在天花板上。博物馆的顾客走过博物馆,能够嬉戏地击打气球以放弃沉没。
以后,咱们的应用程序很像“银云”的房间。能够很乏味地单击图标并在应用程序 shell
中浏览,然而最终这只是一个空荡荡的房间。在本章中,咱们将通过首先摸索如何应用 React Native
的列表视图显示内容来开始填充咱们的应用程序。而后,咱们将应用 Apollo Client
连贯到咱们的数据 API
。建设连贯后,咱们将编写GraphQL
查问,这些查问将在应用程序屏幕上显示数据。
在本地运行咱们的 API
咱们的挪动应用程序的开发须要拜访咱们的 API
的本地实例。如果你始终在浏览本书,那么你应该曾经在计算机上启动了 Notedly API
及其数据库。
如果没有,我在本书的附录 A
中增加了无关如何获取并运行 API
正本以及一些示例数据的阐明。如果你曾经在运行 API
了,然而心愿应用其余数据,请从 API
我的项目目录的根目录运行npm run seed
。
创立列表和可滚动内容视图
列表无处不在。在生活中,咱们保留待办事项清单、杂货清单和来宾清单。在应用程序中,列表是最常见的 UI
模式之一:社交媒体帖子列表、文章列表、歌曲列表、电影列表等。清单(看我在那儿做了什么?)始终在持续。那么,React Native
将创立可滚动内容列表变成一个简略的过程就难能可贵了。
这两种类型在 React Native
是FlatList
和 SectionList
。FlatList
在一个大量我的项目的滚动列表是十分有用的。
React Native
在幕后做了一些有用的事件,例如仅渲染最后可见的我的项目以进步性能。SectionList
十分相似于FlatList
,只是它容许列表项组具备题目。
考虑一下联系人列表中的联系人,这些列表通常按字母数字程序来排序分组。
出于咱们的目标,咱们将应用 FlatList
显示笔记列表,用户能够滚动查看并单击浏览以浏览残缺的笔记。为此,咱们创立一个名为 NoteFeed
的新组件,该组件可用于显示笔记列表。目前,咱们是应用一些代替数据,然而咱们将很快连贯到咱们的API
。
首先,让咱们在 src/components/NoteFeed.js
中创立一个新组件。咱们将从导入依赖项并增加一系列长期数据开始。
import React from 'react';
import {FlatList, View, Text} from 'react-native';
import styled from 'styled-components/native';
// our dummy data
const notes = [{ id: 0, content: 'Giant Steps'},
{id: 1, content: 'Tomorrow Is The Question'},
{id: 2, content: 'Tonight At Noon'},
{id: 3, content: 'Out To Lunch'},
{id: 4, content: 'Green Street'},
{id: 5, content: 'In A Silent Way'},
{id: 6, content: 'Lanquidity'},
{id: 7, content: 'Nuff Said'},
{id: 8, content: 'Nova'},
{id: 9, content: 'The Awakening'}
];
const NoteFeed = () => {// our component code will go here};
export default NoteFeed;
当初咱们能够编写组件代码,其中将蕴含FlatList
:
const NoteFeed = props => {
return (<View> <FlatList data={notes} keyExtractor={({id}) => id.toString()}
renderItem={({item}) => <Text>{item.content}</Text>}
/> </View> );
};
在后面的代码中,你能够看到 FlatList
接管三个属性,这些属性简化了对数据进行迭代的过程:
data
此属性指向列表将蕴含的数据数组。
keyExtractor
列表中的每个我的项目都必须具备惟一的键值。咱们正在应用 keyExtractor
将惟一的 id
值用作key
。
renderItem
此属性定义应在列表中出现的内容。当初,咱们要从 notes
数组传递单个我的项目,并将其显示为Text
。
咱们能够通过更新 src/screens/feed.js
组件以显示数据来查看列表:
import React from 'react';
// import NoteFeed
import NoteFeed from '../components/NoteFeed';
const Feed = props => {return <NoteFeed />;};
Feed.navigationOptions = {title: 'Feed'};
export default Feed;
让咱们回到 src/components/NoteFeed.js
文件,并更新 renderItem
以应用款式化的组件在列表项之间增加一些间距:
// FeedView styled component definition
const FeedView = styled.View`
height: 100;
overflow: hidden;
margin-bottom: 10px;
`;
const NoteFeed = props => {
return (<View> <FlatList data={notes} keyExtractor={({id}) => id.toString()}
renderItem={({item}) => (<FeedView> <Text>{item.content}</Text> </FeedView> )}
/> </View> );
};
如果你浏览咱们的应用程序,则会看到可滚动的数据列表。最初,咱们能够在列表项之间增加分隔符。通过应用 React Native
,无需通过CSS
增加底部边框,而是能够将 ItemSeparatorComponent
属性传递给 FlatList
。这使咱们能够进行实现细粒度的管制,将任何类型的组件作为分隔符搁置在列表元素之间。它还能够防止在不须要的中央搁置分隔符,例如在列表中的最初一项之后。为了咱们的目标,咱们将增加一个简略的边框,将其作为款式化的组件View
创立:
// FeedView styled component definition
const FeedView = styled.View`
height: 100;
overflow: hidden;
margin-bottom: 10px;
`;
// add a Separator styled component
const Separator = styled.View`
height: 1;
width: 100%;
background-color: #ced0ce;
`;
const NoteFeed = props => {
return (<View> <FlatList data={notes} keyExtractor={({id}) => id.toString()}
ItemSeparatorComponent={() => <Separator />}
renderItem={({item}) => (<FeedView> <Text>{item.content}</Text> </FeedView> )}
/> </View> );
};
与其间接在 FlatList
中出现和款式化笔记的内容,不如将其隔离在其本人的组件中。为此,咱们将介绍一种称为ScrollView
。
ScrollView
的性能正是你所冀望的:ScrollView
不会合乎屏幕的大小,而是会溢出内容,从而容许用户滚动。
让咱们在 src/components/Note.js
中创立一个新组件:
import React from 'react';
import {Text, ScrollView} from 'react-native';
import styled from 'styled-components/native';
const NoteView = styled.ScrollView`
padding: 10px;
`;
const Note = props => {
return (<NoteView> <Text>{props.note.content}</Text> </NoteView>
);
};
export default Note;
最初,咱们将导入 src/components/NoteFeed.js
组件,并在 FeedView
中应用它,以应用新的 Note
组件。最终的组件代码如下(图23-1
):
import React from 'react';
import {FlatList, View, Text} from 'react-native';
import styled from 'styled-components/native';
import Note from './Note';
// our dummy data
const notes = [{ id: 0, content: 'Giant Steps'},
{id: 1, content: 'Tomorrow Is The Question'},
{id: 2, content: 'Tonight At Noon'},
{id: 3, content: 'Out To Lunch'},
{id: 4, content: 'Green Street'},
{id: 5, content: 'In A Silent Way'},
{id: 6, content: 'Lanquidity'},
{id: 7, content: 'Nuff Said'},
{id: 8, content: 'Nova'},
{id: 9, content: 'The Awakening'}
];
// FeedView styled-component definition
const FeedView = styled.View`
height: 100;
overflow: hidden;
margin-bottom: 10px;
`;
const Separator = styled.View`
height: 1;
width: 100%;
background-color: #ced0ce;
`;
const NoteFeed = props => {
return (
<View>
<FlatList
data={notes}
keyExtractor={({id}) => id.toString()}
ItemSeparatorComponent={() => <Separator />}
renderItem={({item}) => (
<FeedView>
<Note note={item} />
</FeedView>
)}
/>
</View>
);
};
export default NoteFeed;
图23-1
。应用FlatList
,咱们能够显示数据列表
有了这个,咱们安排了一个简略的FlatList
。当初,实现使从列表项路由到单个路由。
使列表可路由
在挪动应用程序中,一种十分常见的模式是点击列表中的我的项目以查看更多信息或扩大性能。如果你回想起前一章,则咱们的摘要位于笔记屏幕上方的导航列表中。
在 React Native
中,咱们能够应用 TouchableOpacity
作为包装器来使任何视图响应用户的点击。
这意味着咱们能够在 TouchableOpacity
包裹咱们的 FeedView
的内容和点击切换路由。就如同咱们过来用按钮一样的形式。
让咱们持续更新咱们的 src/components/NoteFeed.js
组件来做到这一点。
首先,咱们必须更新咱们的 react-native import
,在src/components/NoteFeed.js
引入TouchableOpacity
import {FlatList, View, TouchableOpacity} from 'react-native';
接下来,咱们更新组件以应用TouchableOpacity
:
const NoteFeed = props => {
return (
<View>
<FlatList data={notes} keyExtractor={({id}) => id.toString()}
ItemSeparatorComponent={() => <Separator />}
renderItem={({item}) => (<TouchableOpacity onPress={() =>
props.navigation.navigate('Note', {id: item.id})
}
>
<FeedView>
<Note note={item} />
</FeedView>
</TouchableOpacity>
)}
/>
</View>
);
};
咱们还须要更新 feed.js
屏幕组件,以将导航属性传递到 feed
。在src/screens/feed.js
中:
const Feed = props => {return <NoteFeed navigation={props.navigation} />;
};
这样,咱们能够轻松导航到通用笔记屏幕。让咱们自定义该屏幕,以便它显示笔记的 ID
。你可能曾经留神到,在NoteFeed
组件导航中,咱们传递了 id
属性。在 screens/note.js
中,咱们能够读取该属性的值:
import React from 'react';
import {Text, View} from 'react-native';
const NoteScreen = props => {const id = props.navigation.getParam('id');
return (<View style={{ padding: 10}}> <Text>This is note {id}</Text> </View>
);
};
export default NoteScreen;
当初,咱们可用于从列表视图导航到详细信息页面。接下来,让咱们看一下如何将 API
中的数据集成到应用程序中。
带有 Apollo 客户端的 GraphQL
至此,咱们筹备在应用程序中读取和显示数据。咱们将拜访在本书第一局部中创立的 GraphQL API
。咱们将应用Apollo Client
,这是本书Web
局部中的 GraphQL
客户端的库。Apollo Client
提供了许多有用的性能,以简化在 JavaScript UI
应用程序中应用 GraphQL
的工作。Apollo
的客户端性能包含从近程 API
获取数据、本地缓存、GraphQL
语法解决、本地状态治理等。
首先,咱们首先须要设置咱们的配置文件。咱们将环境变量存储在名为 config.js
的文件中。在 React Native
中,有几种办法能够治理环境和配置变量,然而我发现这种款式的配置文件是最间接、最无效的。首先,我蕴含了 config-example.js
文件,你能够应用咱们的应用程序值进行复制和编辑。在终端应用程序中,从我的项目目录的根目录:
$ cp config.example.js config.js
从这里咱们能够更新任何 dev
或prod
环境变量。在目前的状况下,这只是生产API
_URI 值:
// set environment variables
const ENV = {
dev: {API_URI: `http://${localhost}:4000/api`
},
prod: {
// update the API_URI value with your publicly deployed API address
API_URI: 'https://your-api-uri/api'
}
};
当初,咱们将基于 Expo
的环境可能应用 getEnvVars
函数拜访这两个值。咱们不会深入研究配置文件的其余部分,然而如果你有趣味进一步摸索此设置,请对它进行摸索。
从这里咱们能够将客户端连贯到咱们的 API
。在咱们的src/Main.js
文件中,咱们将应用 Apollo
客户端库设置Apollo
。如果你实现了本书的网络局部,这将十分相熟:
import React from 'react';
import Screens from './screens';
// import the Apollo libraries
import {ApolloClient, ApolloProvider, InMemoryCache} from '@apollo/client';
// import environment configuration
import getEnvVars from '../config';
const {API_URI} = getEnvVars();
// configure our API URI & cache
const uri = API_URI;
const cache = new InMemoryCache();
// configure Apollo Client
const client = new ApolloClient({
uri,
cache
});
const Main = () => {
// wrap our app in the ApolloProvider higher-order component
return (<ApolloProvider client={client}> <Screens /> </ApolloProvider>
);
};
export default Main;
这样,咱们的应用程序就不会有显著的变动,咱们当初曾经连贯到咱们的 API
。接下来,让咱们看看如何从该API
查问数据。
编写 GraphQL
查问
当初咱们曾经连贯到咱们的 API
,让咱们查问一些数据。咱们将从查询数据库中所有要显示在NoteFeed
列表中的笔记开始。而后,咱们将查问要在“笔记详细信息”视图中显示的各个笔记。
笔记查问
为了简化并缩小反复,咱们将应用批量笔记 API
查问而不是分页的 noteFeed
查问。编写查问组件的工作形式与在 React Web
应用程序中完全相同。在 src/screens/feed.js
中,咱们导入 useQuery
和GraphQL
语言(gql
)库,如下所示:
// import our React Native and Apollo dependencies
import {Text} from 'react-native';
import {useQuery, gql} from '@apollo/client';
接下来,咱们编写查问:
const GET_NOTES = gql`
query notes {
notes {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
`;
最初,咱们更新组件以调用查问:
const Feed = props => {const { loading, error, data} = useQuery(GET_NOTES);
// if the data is loading, our app will display a loading indicator
if (loading) return <Text>Loading</Text>;
// if there is an error fetching the data, display an error message
if (error) return <Text>Error loading notes</Text>;
// if the query is successful and there are notes, return the feed of notes
return <NoteFeed notes={data.notes} navigation={props.navigation} />;
};
总之,咱们的 src/screens/feed.js
文件编写如下:
import React from 'react';
import {Text} from 'react-native';
// import our Apollo libraries
import {useQuery, gql} from '@apollo/client';
import NoteFeed from '../components/NoteFeed';
import Loading from '../components/Loading';
// compose our query
const GET_NOTES = gql`
query notes {
notes {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
`;
const Feed = props => {const { loading, error, data} = useQuery(GET_NOTES);
// if the data is loading, our app will display a loading indicator
if (loading) return <Text>Loading</Text>;
// if there is an error fetching the data, display an error message
if (error) return <Text>Error loading notes</Text>;
// if the query is successful and there are notes, return the feed of notes
return <NoteFeed notes={data.notes} navigation={props.navigation} />;
};
Feed.navigationOptions = {title: 'Feed'};
export default Feed;
编写查问后,咱们能够更新 src/components/NoteFeed.js
组件以应用通过 props
传递给它的数据:
const NoteFeed = props => {
return (
<View>
<FlatList data={props.notes} keyExtractor={({id}) => id.toString()}
ItemSeparatorComponent={() => <Separator />}
renderItem={({item}) => (<TouchableOpacity onPress={() =>
props.navigation.navigate('Note', {id: item.id})
}
>
<FeedView>
<Note note={item} />
</FeedView>
</TouchableOpacity>
)}
/>
</View>
);
};
进行此更改后,在运行 Expo
的状况下,咱们将在列表中看到本地 API
的数据,如图 23-2
所示。
图 23-2
。咱们的API
数据显示在咱们的 Feed
视图中
当初,在列表中点击笔记预览仍会显示惯例笔记页面。让咱们通过在 src/screens/note.js
文件中进行笔记查问来解决该问题:
import React from 'react';
import {Text} from 'react-native';
import {useQuery, gql} from '@apollo/client';
import Note from '../components/Note';
// our note query, which accepts an ID variable
const GET_NOTE = gql`
query note($id: ID!) {note(id: $id) {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
`;
const NoteScreen = props => {const id = props.navigation.getParam('id');
const {loading, error, data} = useQuery(GET_NOTE, { variables: { id} });
if (loading) return <Text>Loading</Text>;
// if there's an error, display this message to the user
if (error) return <Text>Error! Note not found</Text>;
// if successful, pass the data to the note component
return <Note note={data.note} />;
};
export default NoteScreen;
最初,让咱们更新 src/components/Note
组件文件以显示笔记内容。咱们将增加两个新的依赖关系,react-native-markdown-renderer
和 date-fns
,以一种更加用户敌对的形式来解析API
中的 Markdown
和日期。
import React from 'react';
import {Text, ScrollView} from 'react-native';
import styled from 'styled-components/native';
import Markdown from 'react-native-markdown-renderer';
import {format} from 'date-fns';
const NoteView = styled.ScrollView`
padding: 10px;
`;
const Note = ({note}) => {
return (
<NoteView>
<Text>
Note by {note.author.username} / Published{' '}
{format(new Date(note.createdAt), 'MMM do yyyy')}
</Text>
<Markdown>{note.content}</Markdown>
</NoteView>
);
};
export default Note;
进行了这些更改后,咱们将在应用程序的 feed
视图中看到笔记列表。轻按笔记预览将带咱们进入笔记的全副可滚动内容(请参见图23-3
)。
图 23-3
。通过编写GraphQL
查问,咱们能够在屏幕之间导航以查看笔记预览和残缺笔记
增加加载指示器
以后,当在咱们的应用程序中加载数据时,它会在屏幕上闪动“Loading
”字样。这可能在传播音讯时很无效,但同时也会给用户带来极大的挫伤。React Native
为咱们提供了一个内置的ActivityIndicator
,它能够显示适宜操作系统的加载指示器。让咱们编写一个简略的组件,将其用作应用程序中的加载指示器。
在 src/components/Loading.js
中创立一个文件,并组成一个简略的组件,该组件在屏幕核心显示流动指示器:
import React from 'react';
import {View, ActivityIndicator} from 'react-native';
import styled from 'styled-components/native';
const LoadingWrap = styled.View`
flex: 1;
justify-content: center;
align-items: center;
`;
const Loading = () => {
return (
<LoadingWrap>
<ActivityIndicator size="large" />
</LoadingWrap>
);
};
export default Loading;
当初,咱们能够替换 GraphQL
查问组件中的“正在加载”文本。在 src/screens/feed.js
和src/screens/note.js
中,首先导入 Loading
组件:
import Loading from '../components/Loading';
而后,在两个文件中,如下更新 Apollo
加载状态:
if (loading) return <Loading />;
这样,当咱们的 API
数据正在加载时,咱们的应用程序当初将显示旋转流动指示器(请参见图23-4
)。
图23-4
。应用ActivityIndicator
,咱们能够增加操作系统 - 适当的加载指示器
论断
在本章中,咱们首先钻研了如何应用常见的应用程序 UI
模式将列表视图集成到 React Native
应用程序中。在此,咱们配置了 Apollo Client
,并将来自API
的数据集成到应用程序中。有了这个,咱们曾经领有构建许多常见类型的应用程序所需的所有,例如新闻应用程序或集成来自网站的博客。在下一章中,咱们将向咱们的应用程序增加身份验证并显示特定于用户的查问。
如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦您点赞珍藏或者分享一下,心愿能够帮到更多人。