Last active
December 25, 2018 20:41
-
-
Save ericallam/51ae9fbd654d8b692bff to your computer and use it in GitHub Desktop.
Getting React Native's Navigator and Relay to work together
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AppComponent from './src/components/AppComponent'; | |
import Relay, { | |
DefaultNetworkLayer, | |
} from 'react-relay'; | |
import React, { | |
Component | |
} from 'react-native'; | |
Relay.injectNetworkLayer(new DefaultNetworkLayer('http://localhost:3000/graphql')); | |
export default class App extends Component { | |
render(): void { | |
return ( | |
<AppComponent /> | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Relay, { | |
RootContainer, | |
Route | |
} from 'react-relay' | |
class SeasonRoute extends Route { | |
static paramDefinitions = {}; | |
static queries = { | |
currentSeason: () => Relay.QL`query { currentSeason }`, | |
}; | |
static routeName = 'MatchdayRoute'; | |
} | |
class NodeRoute extends Route { | |
static paramDefinitions = { | |
nodeID: { required: true } | |
}; | |
static queries = { | |
node: () => Relay.QL`query { node(id: $nodeID) }`, | |
}; | |
static routeName = 'NodeRoute'; | |
} | |
const MatchdayList = require('./MatchdayList'); | |
const MatchList = require('./MatchList'); | |
const ROUTES = { | |
MatchdayList, | |
MatchList | |
}; | |
import React, { | |
View, | |
Text, | |
StyleSheet, | |
Navigator, | |
Component | |
} from 'react-native'; | |
export default class AppComponent extends Component { | |
renderScene(route, navigator){ | |
const props = { route, navigator }; | |
if (route.name == "MatchdayList") { | |
return <RootContainer | |
Component={MatchdayList} | |
route={new SeasonRoute()} | |
renderFetched={(data) => <MatchdayList {...props} {...data} />} | |
/> | |
}else if (route.name == "MatchList") { | |
return <RootContainer | |
Component={MatchList} | |
route={new NodeRoute({nodeID: route.matchday.id})} | |
renderFetched={(data) => <MatchList {...props} {...data} />} | |
/> | |
} | |
} | |
render() { | |
return ( | |
<Navigator | |
style = { styles.container } | |
initialRoute = { { name: 'MatchdayList' } } | |
renderScene = { this.renderScene.bind(this) } | |
configureScene = { () => { return Navigator.SceneConfigs.FloatFromRight; } } /> | |
); | |
} | |
} | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
justifyContent: 'center' | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { | |
AppRegistry, | |
} from 'react-native'; | |
import App from './app'; | |
AppRegistry.registerComponent('App', () => App); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Relay from 'react-relay'; | |
import React, { | |
View, | |
Text, | |
StyleSheet, | |
Component, | |
TouchableHighlight | |
} from 'react-native'; | |
class MatchdayItem extends Component { | |
constructor(props, context) { | |
super(props, context); | |
this._handleItemPress = this._handleItemPress.bind(this); | |
} | |
_handleItemPress() { | |
this.props.handleItemPress(this.props.matchday) | |
} | |
render() { | |
var { matchday } = this.props; | |
return ( | |
<TouchableHighlight onPress={this._handleItemPress}> | |
<View> | |
<View> | |
<Text> | |
Matchday {matchday.number} | |
</Text> | |
<Text> | |
{matchday.start_date} | |
</Text> | |
<Text> | |
{matchday.status} | |
</Text> | |
</View> | |
<View> | |
<Text>{matchday.matches_count} matches</Text> | |
</View> | |
</View> | |
</TouchableHighlight> | |
) | |
} | |
} | |
module.exports = Relay.createContainer(MatchdayItem, { | |
fragments: { | |
matchday: () => Relay.QL` | |
fragment on Matchday { | |
id, | |
number, | |
start_date, | |
matches_count, | |
status | |
} | |
` | |
}, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Relay from 'react-relay'; | |
import MatchdayItem from './MatchdayItem'; | |
import MatchList from './MatchList' | |
import React, { | |
View, | |
Text, | |
StyleSheet, | |
ListView, | |
Component | |
} from 'react-native'; | |
const _matchdaysDataSource = new ListView.DataSource({ | |
rowHasChanged: (r1, r2) => r1.__dataID__ !== r2.__dataID__, | |
}); | |
class MatchdayList extends Component { | |
constructor(props, context) { | |
super(props, context); | |
const { edges } = props.currentSeason.matchdays; | |
this.state = { | |
initialListSize: edges.length, | |
listScrollEnabled: true, | |
dataSource: _matchdaysDataSource.cloneWithRows(edges), | |
}; | |
this.renderMatchdayEdge = this.renderMatchdayEdge.bind(this); | |
this.handleItemPress = this.handleItemPress.bind(this); | |
} | |
handleItemPress(matchday) { | |
this.props.navigator.push({name: 'MatchList', matchday: matchday}); | |
} | |
componentWillReceiveProps(nextProps) { | |
if (this.props.currentSeason.matchdays.edges !== nextProps.currentSeason.matchdays.edges) { | |
const { | |
dataSource, | |
} = this.state; | |
this.setState({ | |
dataSource: | |
dataSource.cloneWithRows(nextProps.currentSeason.matchdays.edges), | |
}); | |
} | |
} | |
renderSeparator(sectionId, rowId) { | |
return <View key={`sep_${sectionId}_${rowId}`} style={styles.separator} />; | |
} | |
renderMatchdayEdge(matchdayEdge, sectionID, rowID) { | |
return ( | |
<MatchdayItem | |
key={matchdayEdge.node.id} | |
matchday={matchdayEdge.node} | |
handleItemPress={this.handleItemPress} | |
/> | |
); | |
} | |
render() { | |
return ( | |
<View> | |
<View> | |
<Text>{currentSeason.name}</Text> | |
</View> | |
<ListView | |
dataSource={this.state.dataSource} | |
initialListSize={this.state.initialListSize} | |
renderRow={this.renderMatchdayEdge} | |
renderSeparator={this.renderSeparator} | |
/> | |
</View> | |
); | |
} | |
} | |
module.exports = Relay.createContainer(MatchdayList, { | |
fragments: { | |
currentSeason: () => Relay.QL` | |
fragment on Season { | |
name, | |
matchdays(first: 100) { | |
edges { | |
node { | |
id, | |
number | |
${MatchdayItem.getFragment('matchday')} | |
}, | |
}, | |
}, | |
} | |
` | |
}, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Relay from 'react-relay'; | |
import React, { | |
View, | |
Text, | |
StyleSheet, | |
Component, | |
ListView, | |
TouchableHighlight | |
} from 'react-native'; | |
const MatchResults = ({ | |
home_team_score, | |
away_team_score | |
}) => ( | |
<Text style={styles.score}> | |
{home_team_score} - {away_team_score} | |
</Text> | |
) | |
import moment from 'moment' | |
class MatchItem extends Component { | |
_renderResults() { | |
var { match } = this.props; | |
if (match.status === 'FINISHED') { | |
return <MatchResults | |
home_team_score={match.home_team_score} | |
away_team_score={match.away_team_score} /> | |
} | |
} | |
render() { | |
const { match } = this.props; | |
const scheduledDate = moment(match.scheduled_date); | |
return ( | |
<TouchableHighlight> | |
<View style={styles.row}> | |
<View style={styles.info}> | |
<Text style={styles.teams}> | |
{match.home_team.name} vs {match.away_team.name} | |
</Text> | |
<Text style={styles.date}> | |
{scheduledDate.format('DD/MM/YYYY h:mm:ss a')} | |
</Text> | |
{this._renderResults()} | |
</View> | |
</View> | |
</TouchableHighlight> | |
) | |
} | |
} | |
MatchItem = Relay.createContainer(MatchItem, { | |
fragments: { | |
match: () => Relay.QL` | |
fragment on Match { | |
id, | |
scheduled_date, | |
status, | |
home_team_score, | |
away_team_score, | |
home_team { name } | |
away_team { name } | |
} | |
` | |
}, | |
}); | |
const _matchesDataSource = new ListView.DataSource({ | |
rowHasChanged: (r1, r2) => r1.__dataID__ !== r2.__dataID__, | |
}); | |
class MatchList extends Component { | |
constructor(props, context) { | |
super(props, context); | |
const { edges } = props.node.matches; | |
this.state = { | |
initialListSize: edges.length, | |
listScrollEnabled: true, | |
dataSource: _matchesDataSource.cloneWithRows(edges), | |
}; | |
this.renderMatchEdge = this.renderMatchEdge.bind(this); | |
} | |
componentWillReceiveProps(nextProps) { | |
if (this.props.node.matches.edges !== nextProps.node.matches.edges) { | |
const { | |
dataSource, | |
} = this.state; | |
this.setState({ | |
dataSource: | |
dataSource.cloneWithRows(nextProps.node.matches.edges), | |
}); | |
} | |
} | |
renderSeparator(sectionId, rowId) { | |
return <View key={`sep_${sectionId}_${rowId}`} style={styles.separator} />; | |
} | |
renderMatchEdge(matchEdge, sectionID, rowID) { | |
return ( | |
<MatchItem | |
key={matchEdge.node.id} | |
match={matchEdge.node} | |
/> | |
); | |
} | |
render() { | |
var { matchday } = this.props.route; | |
return ( | |
<View style={styles.container}> | |
<View style={styles.header}> | |
<Text style={styles.headerText}>Matchday {matchday.number}</Text> | |
</View> | |
<ListView | |
dataSource={this.state.dataSource} | |
initialListSize={this.state.initialListSize} | |
renderRow={this.renderMatchEdge} | |
renderSeparator={this.renderSeparator} | |
/> | |
</View> | |
); | |
} | |
} | |
module.exports = Relay.createContainer(MatchList, { | |
fragments: { | |
node: () => Relay.QL` | |
fragment on Matchday { | |
number, | |
matches(first: 10) { | |
edges { | |
node { | |
id | |
${MatchItem.getFragment("match")} | |
}, | |
}, | |
}, | |
} | |
` | |
}, | |
}); | |
var styles = StyleSheet.create({ | |
container: { | |
flex: 1 | |
}, | |
separator: { | |
height: 1, | |
backgroundColor: '#e0e0e0', | |
marginLeft: 14 | |
}, | |
header: { | |
height: 60, | |
justifyContent: 'center', | |
alignItems: 'center', | |
backgroundColor: 'lightgrey', | |
flexDirection: 'column', | |
paddingTop: 25 | |
}, | |
headerText: { | |
fontWeight: 'normal', | |
fontSize: 18, | |
color: 'black' | |
}, | |
teams: { | |
fontWeight: 'bold' | |
}, | |
date: { | |
color: '#949494' | |
}, | |
score: { | |
color: '#585858', | |
fontSize: 12, | |
fontWeight: '200' | |
}, | |
row: { | |
flexDirection: 'row', | |
justifyContent: 'space-between', | |
alignItems: 'center', | |
padding: 12, | |
backgroundColor: 'white' | |
}, | |
info: { | |
flexDirection: 'column', | |
justifyContent: 'space-around' | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
scalar DateTime | |
type Match implements Node { | |
id: ID! | |
scheduled_date: DateTime | |
status: String | |
home_team_score: Int | |
away_team_score: Int | |
home_team: Team | |
away_team: Team | |
} | |
type MatchConnection { | |
pageInfo: PageInfo! | |
edges: [MatchEdge] | |
} | |
type Matchday implements Node { | |
id: ID! | |
number: Int! | |
status: Status! | |
matches_count: Int | |
start_date: DateTime | |
end_date: DateTime | |
matches(after: String, first: Int, before: String, last: Int): MatchConnection | |
} | |
type MatchdayConnection { | |
pageInfo: PageInfo! | |
edges: [MatchdayEdge] | |
} | |
type MatchdayEdge { | |
node: Matchday | |
cursor: String! | |
} | |
type MatchEdge { | |
node: Match | |
cursor: String! | |
} | |
interface Node { | |
id: ID! | |
} | |
type PageInfo { | |
hasNextPage: Boolean! | |
hasPreviousPage: Boolean! | |
startCursor: String | |
endCursor: String | |
} | |
type Root { | |
currentSeason: Season | |
node(id: ID!): Node | |
} | |
type Season implements Node { | |
id: ID! | |
name: String! | |
leagueCode: String! | |
matchdays(after: String, first: Int, before: String, last: Int): MatchdayConnection | |
} | |
enum Status { | |
UPCOMING | |
ACCEPTING_UPDATES | |
IN_PROGRESS | |
COMPLETED | |
} | |
type Team implements Node { | |
id: ID! | |
name: String | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've updated this Gist with the recommendation in this relay issue. Now I use a new Route/RootContainer for each Navigator child.