Created
December 20, 2016 10:07
-
-
Save fdecampredon/74501a07ab9a732f6d42bd1c27607cb2 to your computer and use it in GitHub Desktop.
React-router + NavigationExperimental
This file contains hidden or 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, { PropTypes } from 'react'; | |
import { | |
AppRegistry, | |
StyleSheet, | |
Text, | |
View, | |
NavigationExperimental, | |
} from 'react-native'; | |
import { MemoryRouter as Router } from 'react-router'; | |
import { Stack, StackMatch, Link } from './stack'; | |
const { Header: NavigationHeader } = NavigationExperimental; | |
const A = () => ( | |
<View style={styles.scene}> | |
<Text style={styles.welcome}> | |
A | |
</Text> | |
<Link to="/b" label="Go to B" /> | |
</View> | |
); | |
const B = () => ( | |
<View style={styles.scene}> | |
<Text style={styles.welcome}> | |
B | |
</Text> | |
<Link to="/c" label="Go to C" /> | |
</View> | |
); | |
const C = () => ( | |
<View style={styles.scene}> | |
<Text style={styles.welcome}> | |
C | |
</Text> | |
<Link to="/" label="Go to A" /> | |
</View> | |
); | |
const Header = ({ pathname, ...props }, { history }) => ( | |
<NavigationHeader | |
{...props} | |
renderTitleComponent={() => ( | |
<NavigationHeader.Title> | |
{pathname} | |
</NavigationHeader.Title> | |
)} | |
onNavigateBack={() => history.goBack()} | |
/> | |
); | |
Header.contextTypes = { | |
history: PropTypes.object.isRequired, | |
}; | |
Header.propTypes = { | |
pathname: PropTypes.string.isRequired, | |
}; | |
const App = () => ( | |
<Router> | |
<Stack style={styles.container} enableGestures={false}> | |
<StackMatch exactly pattern="/" component={A} headerComponent={Header} /> | |
<StackMatch exactly pattern="/b" component={B} headerComponent={Header} /> | |
<StackMatch exactly pattern="/C" component={C} headerComponent={Header} /> | |
</Stack> | |
</Router> | |
); | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
}, | |
scene: { | |
flex: 1, | |
justifyContent: 'center', | |
alignItems: 'center', | |
backgroundColor: '#FFF', | |
}, | |
}); | |
AppRegistry.registerComponent('ReactRouterStack', () => App); |
This file contains hidden or 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, { PropTypes, Children } from 'react'; | |
import { NavigationExperimental, TouchableOpacity, Text } from 'react-native'; | |
import matchPattern from 'react-router/matchPattern'; | |
const { CardStack: NavigationCardStack } = NavigationExperimental; | |
export function StackMatch() { | |
throw new Error('Stack Match should not be renderered use `Stack`'); | |
} | |
StackMatch.propTypes = { | |
pattern: PropTypes.string.isRequired, | |
exactly: PropTypes.bool, | |
renderHeader: PropTypes.func, | |
headerComponent: PropTypes.func, | |
render: PropTypes.func, | |
component: PropTypes.func, | |
}; | |
export class Stack extends React.Component { | |
static contextTypes = { | |
match: PropTypes.object, | |
history: PropTypes.object.isRequired, | |
} | |
static propTypes = { | |
children: PropTypes.node.isRequired, | |
} | |
constructor(props, context) { | |
super(props, context); | |
const { history } = context; | |
this.historyListener = history.listen(() => { | |
this.setState({ | |
navigationState: this.createNavigationSate(), | |
}); | |
}); | |
this.state = { | |
navigationState: this.createNavigationSate(), | |
}; | |
} | |
componentWillReceiveProps({ children }) { | |
if (children !== this.props.children) { | |
this.setState({ | |
navigationState: this.createNavigationSate(), | |
}); | |
} | |
} | |
componentWillUnmount() { | |
this.historyListener(); | |
} | |
createNavigationSate() { | |
const { history, match: matchContext } = this.context; | |
const children = Children.toArray(this.props.children); | |
const parent = matchContext && matchContext.parent; | |
const routes = history.entries.slice(0, history.index + 1) | |
.map(location => ({ | |
key: location.key || location.pathname, | |
location, | |
child: children.find((child) => { | |
const { pattern, exactly: matchExactly } = child.props; | |
return matchPattern(pattern, location, matchExactly, parent); | |
}), | |
})) | |
.reduce((array, item) => { | |
if (!array.length || array[array.length - 1].child !== item.child) { | |
array.push(item); | |
} | |
return array; | |
}, []); | |
return { index: routes.length - 1, routes }; | |
} | |
onNavigateBack = () => { | |
this.context.history.goBack(); | |
} | |
renderHeader = (sceneProps) => { | |
const { scene: { route: { child, location } } } = sceneProps; | |
const { match: matchContext } = this.context; | |
const parent = matchContext && matchContext.parent; | |
const { | |
pattern, | |
exactly, | |
renderHeader, | |
headerComponent: HeaderComponent, | |
} = child.props; | |
const match = matchPattern(pattern, location, exactly, parent); | |
const props = { ...sceneProps, ...match }; | |
if (renderHeader) { | |
return renderHeader(props); | |
} else if (HeaderComponent) { | |
return ( | |
<HeaderComponent {...props} /> | |
); | |
} | |
return null; | |
} | |
renderScene = (sceneProps) => { | |
const { scene: { route: { child, location } } } = sceneProps; | |
const { match: matchContext } = this.context; | |
const parent = matchContext && matchContext.parent; | |
const { | |
pattern, | |
exactly, | |
render, | |
component: Component, | |
} = child.props; | |
const match = matchPattern(pattern, location, exactly, parent); | |
const props = { ...sceneProps, ...match }; | |
if (render) { | |
return render(props); | |
} | |
return <Component {...props} />; | |
} | |
render() { | |
const { navigationState } = this.state; | |
const { children, ...props } = this.props; // eslint-disable-line no-unused-vars | |
return ( | |
<NavigationCardStack | |
onNavigateBack={this.onNavigateBack} | |
{...props} | |
navigationState={navigationState} | |
renderScene={this.renderScene} | |
renderHeader={this.renderHeader} | |
/> | |
); | |
} | |
} | |
export const Link = ({ to, label, ...props }, context) => ( | |
<TouchableOpacity {...props} onPress={() => context.history.push(to)}> | |
<Text>{label}</Text> | |
</TouchableOpacity> | |
); | |
Link.contextTypes = { | |
history: PropTypes.object.isRequired, | |
}; | |
Link.propTypes = { | |
label: PropTypes.string.isRequired, | |
to: PropTypes.string.isRequired, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment