前一篇拼死拼活的才把 Token 拿回來,這篇要來用 Token 拿資料回來顯示 User 的頁面。
經過一段考慮後,這邊決定使用 Apollo Client 來發送 GraphQL Request,首先必須先安裝 apollo-client
、react-apollo
、graphql-tag
這三個套件:
npm install --save apollo-client react-apollo graphql-tag
或是:
yarn add apollo-client react-apollo graphql-tag
接著用 Github 的 GraphQL API 網址 https://api.github.com/graphql
來建立 Network Interface:
import { createNetworkInterface } from 'apollo-client';
const networkInterface = createNetworkInterface({
uri: 'https://api.github.com/graphql',
});
這邊需要加一個 Middleware 來把 Access Token 加到 Request Header,才能利用使用者授權的權限:
Authorization: Bearer <Access Token>
這 API applyMiddleware(req, next)
很類似 Express 的 Middleware,使用 next
參數來繼續執行下一個 Middleware。要在 Apollo Client 背後的 fetch
上加一些參數,這是絕妙的方式,這邊使用 AsyncStorage.getItem
來拿出 Access Token 以便能塞進 Header:
networkInterface.use([
{
async applyMiddleware(req, next) {
// Create the header object if needed.
if (!req.options.headers) {
req.options.headers = {};
}
const token = await AsyncStorage.getItem('@IronGithub:access_token');
if (token) {
req.options.headers.authorization = `Bearer ${JSON.parse(token).access_token}`;
}
next();
},
},
]);
把前面的 networkInterface
丟進 ApolloClient
當參數就能建構出我們要的 Client:
import ApolloClient from 'apollo-client';
const client = new ApolloClient({
networkInterface,
});
最後在用 ApolloProvider
來把 client
放進 React Context,就先告一段落囉:
import { ApolloProvider } from 'react-apollo';
export default class LoggedIn extends Component {
render() {
return (
<ApolloProvider client={client}>
<UserScreen />
</ApolloProvider>
);
}
}
這次要做的 Component 必須拿登入的使用者資料 (viewer),其中必須有:
followers
跟following
都是一種叫做Connection
的 Type,有興趣的讀者可以看官方網站的這裡,在這邊只要先知道可以從totalCount
拿到總數就好
所以我們會奏出以下的 Query:
query {
viewer {
avatarURL
login
name
followers {
totalCount
}
following {
totalCount
}
}
}
先用 GraphiQL 試試看:
成功!API 沒問題的話就可以開始實作了。
實務上可能是先決定 Feature,然後有 UI 上的需求才會回來 Query 加欄位
在 ApolloProvider
裡面像下面範例這樣利用 graphql(query)
Higher-Order Component 來發出 GraphQL 的 Request 取回資料:
// UserScreen.js
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
class UserScreen extends Component {
render() {
// ...
}
}
const query = gql`query {
viewer {
avatarURL
login
name
followers {
totalCount
}
following {
totalCount
}
}
}`;
export default graphql(query)(UserScreen);
如此一來這些資料就會自己查完丟進來 Component,所以我們可以看到 propTypes
跟 GraphQL Query 有九成像:
class UserScreen extends Component {
static propTypes = {
data: PropTypes.shape({
loading: PropTypes.bool.isRequired,
error: PropTypes.object,
viewer: PropTypes.shape({
avatarURL: PropTypes.string.isRequired,
login: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
followers: PropTypes.shape({
totalCount: PropTypes.number.isRequired,
}).isRequired,
following: PropTypes.shape({
totalCount: PropTypes.number.isRequired,
}).isRequired,
}),
}).isRequired,
};
// ...
}
data 裡面會有 loading
跟 error
的欄位所以可以很方便地顯示載入畫面跟錯誤畫面:
class UserScreen extends Component {
// ...
render() {
const { loading, error, viewer } = this.props.data;
if (loading || error) return null;
return (
<Something />
)
}
}
資料都搞定之後,前端工程師的挑戰才要真正開始。
這邊想特別介紹的是 - react-native-easy-grid,這真的是個簡單快速不囉唆的排版法。
只有三個簡單的 Component Col
, Row
, Grid
:
import { Col, Row, Grid } from 'react-native-easy-grid';
左右切:
<Grid>
<Col></Col>
<Col></Col>
</Grid>
上下切:
<Grid>
<Row></Row>
<Row></Row>
</Grid>
要巢狀或是其他的比例也都很簡單,花個兩三分鐘看個 Readme 就能學會。
this.props.data.viewer
裡面已經有所有需要的資料,所以只要把前面學過的東西一次用出來,用 JSX 加 style
慢慢的
class UserScreen extends Component {
// ...
render() {
const { loading, error, viewer } = this.props.data;
if (loading || error) return null;
return (
<Grid>
<Row size={65} style={styles.profile}>
<Image
style={styles.avatar}
source={{ uri: viewer.avatarURL }}
/>
<Text style={[styles.textCenter, styles.login]}>
{viewer.login}
</Text>
<Text style={[styles.textCenter, styles.name]}>
{viewer.name}
</Text>
</Row>
<Row size={35}>
<Col>
<Text style={[styles.textCenter, styles.followText]}>
<Text style={styles.followNumber}>{viewer.followers.totalCount}</Text> Followers
</Text>
</Col>
<Col>
<Text style={[styles.textCenter, styles.followText]}>
<Text style={styles.followNumber}>{viewer.following.totalCount}</Text> Following
</Text>
</Col>
</Row>
</Grid>
);
}
}
const styles = StyleSheet.create({
textCenter: {
textAlign: 'center',
},
profile: {
flexDirection: 'column',
justifyContent: 'center',
},
avatar: {
marginTop: 90,
marginBottom: 50,
width: 220,
height: 220,
borderRadius: 20,
},
login: {
flex: 3,
fontSize: 28,
lineHeight: 28,
},
name: {
flex: 2,
fontSize: 22,
lineHeight: 22,
},
followText: {
fontSize: 20,
},
followNumber: {
fontSize: 30,
color: '#EA7A4C',
},
});
成果如下:
各位讀者看到者裡應該感受得出來,GraphQL API 是對前端非常友善的 API,很多複雜的事情都給後端去處理掉。前端工程師這幾年總是被各種新技術跟新語法攪在一起,又要常常加入框架之間的戰爭,還享有六個月就能用新技術重寫一次專案的福利,終於有個機會能輕鬆一點...。