Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved documentation #86

Merged
merged 1 commit into from
Dec 21, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 97 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,71 @@
# react-native-router-flux
# React Native Router [![Join the chat at https://gitter.im/aksonov/react-native-router-flux](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aksonov/react-native-router-flux?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

[![Join the chat at https://gitter.im/aksonov/react-native-router-flux](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aksonov/react-native-router-flux?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
React Native Router Actions based on exNavigator (https://github.com/exponentjs/ex-navigator)
Router for React Native based on [exNavigator](https://github.com/exponentjs/ex-navigator)

## Why?
- Use Actions to replace/push/pop screens with easy syntax like Actions.login for navigation to login screen
- Forget about passing navigator object to all React elements, use actions from anywhere in your UI code.
- Configure all of your screens ("routes") once (define animations, nav bars, etc.), at one place and then just use short actions commands. For example if you use some special animation for Login screen, you don't need to code it anywhere where an user should be redirected to login screen.
- Use route "schemas" to define common property for some screens. For example some screens are "modal" (i.e. have animation from bottom and have Cancel/Close nav button), so you could define group for them to avoid any code repeatition.
- Hide nav bar for some screens easily
- Use built-in tab bar for some screens (see demo)
- Nested Navigators are supported (i.e. separate navigator for each tab view inside root navigator). push/pop actions will automatically use latest navigator.
## Features
- Define your screens ("routes") and animation transitions in one central location
- Use simple syntax to call transitions anywhere in your code (e.g. `Actions.login`)
- Eliminates the need to pass navigator objects to your screens
- Use a `Schema` to define common properties for a group of screens. For example, you can define a "modal" `Schema` for screens that animate from the bottom of the screen.
- Ability to show/hide navigation bar (see limitations)
- Support for managing a tab bar, using [react-native-tabs](https://github.com/aksonov/react-native-tabs) (see demo)
- Support for nested navigators. For example, each tab can have its own navigator, nested in a root navigator. Transition actions will automatically use the top navigator.

## Redux or other Flux support
The component doesn't depend from any Flux implementation and allows to intercept all route actions by adding Actions.onPush/onReplace/onPop handlers from your store(s).
If handler returns false route action is ignored. For Redux Don't forget to 'connect' your component to your store.
## Installation
```
npm i react-native-router-flux --save
```

## Usage
1. In top-level index.js, define a `Router` and child `Route` elements for your screens.
* If some of your screens have common attributes, consider defining a `Schema` element to reduce repetition
2. In any app screen:
* import {Actions} from 'react-native-router-flux'
* Actions.ACTION_NAME(PARAMS) will call appropriate action and params will be passed to the route

## Configuration

##### Router:
| Property | Type | Default | Description |
|---------------|----------|--------------|----------------------------------------------------------------|
| header | object | null | optional header view |
| footer | object | null | optional footer view (e.g. tab bar) |
| hideNavBar | bool | false | hides navigation bar |

##### Route:

| Property | Type | Default | Description |
|-----------|--------|---------|--------------------------------------------|
| name | string | required | Will be used to call screen transition, for example, `Actions.name(params)`. Must be unique. |
| component | React.Component | semi-required | The `Component` to be displayed. Not required when defining a nested `Router` or child, see example |
| type | string | optional | Defines how the new screen is added to the navigator stack. One of `push`, `replace`, `switch`. Default is 'push'. `replace` tells navigator to replace current route with new route. `switch` is used for tab screens. |
| initial | bool | false | Set to `true` if this is the initial screen |
| title | string | null | The title to be displayed in the navigation bar |
| schema | string | optional | Set this property to the name of a previously defined `Schema` to inherit its properties |
| wrapRouter | bool | false | If `true`, the route is automatically nested in its own `Router`. Useful for modal screens. |
| sceneConfig | Navigator.SceneConfigs | optional | Defines the transition animation. |

##### Schema:

| Property | Type | Default | Description |
|-----------|--------|---------|--------------------------------------------|
| name | string | required | The name of the schema, to be referenced in the route as `schema={"name"}` |
| property | - | - | A `Schema` can have any property that you want the `Route` to inherit |


## Example
![launch](https://cloud.githubusercontent.com/assets/1321329/11692367/7337cfe2-9e9f-11e5-8515-e8b7a9f230ec.gif)

```javascript
var React = require('react-native');
var {AppRegistry, Navigator, StyleSheet,Text,View} = React;
var Launch = require('./components/Launch');
var Register = require('./components/Register');
var Login = require('./components/Login');
var Login2 = require('./components/Login2');
var {Router, Route, Schema, Animations, TabBar} = require('react-native-router-flux');
var Error = require('./components/Error');
var Home = require('./components/Home');
var TabView = require('./components/TabView');
import React, {AppRegistry, Navigator, StyleSheet, Text, View} from 'react-native'
import Launch from './components/Launch'
import Register from './components/Register'
import Login from './components/Login'
import Login2 from './components/Login2'
import {Router, Route, Schema, Animations, TabBar} from 'react-native-router-flux'
import Error from './components/Error'
import Home from './components/Home'
import TabView from './components/TabView'

class TabIcon extends React.Component {
render(){
Expand Down Expand Up @@ -81,12 +116,9 @@ export default class Example extends React.Component {

components/Launch.js (initial screen)
```
'use strict';

var React = require('react-native');
var {View, Text, StyleSheet, TouchableHighlight} = React;
var Button = require('react-native-button');
var Actions = require('react-native-router-flux').Actions;
import React, {View, Text, StyleSheet, TouchableHighlight} from 'react-native'
import Button from 'react-native-button'
import {Actions} from 'react-native-router-flux'

class Launch extends React.Component {
render(){
Expand Down Expand Up @@ -114,16 +146,38 @@ var styles = StyleSheet.create({
module.exports = Launch;
```

## Getting started
1. `npm install react-native-router-flux --save`
2. In top-level index.js:
* Define Route for each app screen. Its 'type' attribute is 'push' by default, but you also could define 'replace', so navigator will replace current route with new route.
'switch' type represents tab screen. 'component' attribute is React component class which will be created for this route and all route attributes will be passed to it.
Instead of defining 'component' class you could define any child (one) you want to be used for that route (even one more Router)
'wrapRouter' - adds Router child for this Route (so defines own navigator for the route). 'name' is unique name of Route.
All other attributes will be passed to scene class.

* If some your Routes have common attributes, you may define Schema element and just use 'schema' attribute for 'route'
3. In any app screen:
* var {Actions} = require('react-native-router-flux');
* Actions.ACTION_NAME(PARAMS) will call appropriate action and params will be passed to the route
## Redux/Flux support
This component is not opinionated and does not depend on any implementation of Flux or Redux.

All route actions can be hooked by adding handlers for `Actions.onPush`, `Actions.onReplace`, `Actions.onPop` in your store(s).

If a handler returns false, the route action is ignored. For Redux, you will need to 'connect' your component to your store.

For example, instead of
```
<Route name="register" component={Register} title="Register"/>
```

you might write:

```
<Route name="register" component={connect(selectFnForRegister)(Register)} title="Register"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really a common practice? I feel like it's more common to call connect in the component file, not inside the routing file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took that from #56. Will you write up an example of how you are doing it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was just reading through that discussion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I've been doing it, which I think is more common practice than using connect inline has been like this:

export default connect((state) => ({
  email: state.data.ui.login.email,
  password: state.data.ui.login.password,
  status: state.data.ui.login.status,
  error: state.data.ui.login.error
}))(Login);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and then when you import the Login component it is already connected

```

## Limitations
### Nested Routers
* Showing and hiding navigation bars and tab bars is only possible on the root `Router`. For example, if you have a tab bar where each tab has its own `Router`, it would not be possible for to show the navigation bar on tab 1, and then push another screen onto the child navigator without the navigation bar shown. In order to get this behavior you will have to manage your own navigation bar component.
* Although you can pass data into a `Route` (e.g. `Actions.login({user_id: '3'})`), this is currently not working for `Route` inside of a nested `Router`.
* If you are using a tab bar where each tab has its own `Router`, modal screens will have to be presented from the root navigator in order to cover the tab bar. To do this, the modal screen should be defined in the root router, and should have the `wrapRouter={true}` property as in the example below.
```
<Router>
<Schema name="modal" sceneConfig={Navigator.SceneConfigs.FloatFromBottom}/>
<Route name="myModal" component={myModal} title="Modal" schema="modal" wrapRouter={true} />
<Route name="tabbar">
<Router footer={TabBar}>
<Route name="tab1" schema="tab" title="Tab 1" component={Tab1}/>
</Router>
</Route>
</Router>
```