48 |
49 |
50 |
51 | {% if site.google_analytics %}
52 |
75 | {% endif %}
76 |
77 |
78 |
--------------------------------------------------------------------------------
/examples/react-native/android/app/src/main/java/com/example/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactInstanceManager;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.soloader.SoLoader;
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.util.List;
13 |
14 | public class MainApplication extends Application implements ReactApplication {
15 |
16 | private final ReactNativeHost mReactNativeHost =
17 | new ReactNativeHost(this) {
18 | @Override
19 | public boolean getUseDeveloperSupport() {
20 | return BuildConfig.DEBUG;
21 | }
22 |
23 | @Override
24 | protected List getPackages() {
25 | @SuppressWarnings("UnnecessaryLocalVariable")
26 | List packages = new PackageList(this).getPackages();
27 | // Packages that cannot be autolinked yet can be added manually here, for example:
28 | // packages.add(new MyReactNativePackage());
29 | return packages;
30 | }
31 |
32 | @Override
33 | protected String getJSMainModuleName() {
34 | return "index";
35 | }
36 | };
37 |
38 | @Override
39 | public ReactNativeHost getReactNativeHost() {
40 | return mReactNativeHost;
41 | }
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 | SoLoader.init(this, /* native exopackage */ false);
47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
48 | }
49 |
50 | /**
51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
53 | *
54 | * @param context
55 | * @param reactInstanceManager
56 | */
57 | private static void initializeFlipper(
58 | Context context, ReactInstanceManager reactInstanceManager) {
59 | if (BuildConfig.DEBUG) {
60 | try {
61 | /*
62 | We use reflection here to pick up the class that initializes Flipper,
63 | since Flipper library is not available in release mode
64 | */
65 | Class> aClass = Class.forName("com.example.ReactNativeFlipper");
66 | aClass
67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
68 | .invoke(null, context, reactInstanceManager);
69 | } catch (ClassNotFoundException e) {
70 | e.printStackTrace();
71 | } catch (NoSuchMethodException e) {
72 | e.printStackTrace();
73 | } catch (IllegalAccessException e) {
74 | e.printStackTrace();
75 | } catch (InvocationTargetException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Simple React Native Routing
2 |
3 | #### WARNING: react-native-router-flux v4 is in beta. Go [here](https://github.com/aksonov/react-native-router-flux/tree/3.39.1) for v3.
4 |
5 | ___
6 |
7 | Define all your routes in one place...
8 |
9 | ```js
10 | class App extends React.Component {
11 | render() {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | ```
24 |
25 | ...and navigate from scene to scene with a simple, powerful API
26 |
27 | ```js
28 | // login.js
29 |
30 | // navigate to 'home' as defined in your top-level router
31 | Actions.home(PARAMS)
32 |
33 | // go back (i.e. pop the current screen off the nav stack)
34 | Actions.pop()
35 |
36 | // refresh the current Scene with the specified props
37 | Actions.refresh({param1: 'hello', param2: 'world'})
38 | ```
39 |
40 |
41 | ## Try the example app
42 |
43 | 
44 |
45 | ```bash
46 | # Get the code
47 | git clone git@github.com:aksonov/react-native-router-flux.git`
48 | cd react-native-router-flux/Example
49 |
50 | # Install dependencies
51 | yarn
52 |
53 | # Run it
54 | react-native run-ios
55 | ```
56 |
57 | ## v4 Features
58 | * Based on latest [React Navigation](https://reactnavigation.org) API
59 | * Separate navigation logic from presentation. You may change now navigation state directly from your business logic code - stores/reducers/etc. navigationStore
60 | * Built-in state machine (former Switch replacement) - each ‘scene’ has onEnter/onExit handlers.
61 | MobX-powered, all used scenes are wrapped as 'observer' automatically. You may subscribe to navigationStore (former Actions), observe current navigation state, etc. If you are using Redux, skip this.
62 | * Flexible nav bar customization, that is not allowed by react navigation right now:
63 | https://github.com/react-community/react-navigation/issues/779
64 | * Drawer support (react
65 | * 'Lightbox' support (used by popups like Error alert within Example project)
66 |
67 | ## Breaking changes (compared to v3):
68 | * No duration/panHandlers support - you have to implement custom navigator now instead and pass it as ‘navigator’ prop:
69 | https://reactnavigation.org/docs/navigators/custom
70 | * No support for partial hiding of tab bar for some tabs because of react navigation bug:
71 | https://github.com/react-community/react-navigation/issues/1584
72 | * No possibility to skip animation during reset/replace:
73 | https://github.com/react-community/react-navigation/issues/1493
74 | * `Switch` is removed - you may use onEnter/onExit handlers for more flexible logic.
75 | * `getSceneStyle` is removed (no needed in v4).
76 | * Custom reducer (`createReducer` prop for Router) - Redux actions now are passed from React Navigation (‘Navigation/BACK’, ‘Navigation/NAVIGATE’, etc.)
77 | * Drawer is 'drawer' attribute Scene
78 | * Modal is 'modal' attribute for Scene
79 | * No flux 'focus' actions - use onEnter/onExit handlers instead.
80 | * Possible other stuff.
81 |
82 | ## Migrating from v3
83 | Coming soon
84 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-router-flux",
3 | "version": "4.3.1",
4 | "description": "React Native Router using Flux architecture",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/aksonov/react-native-router-flux"
8 | },
9 | "bugs": {
10 | "url": "https://github.com/aksonov/react-native-router-flux"
11 | },
12 | "homepage": "https://github.com/aksonov/react-native-router-flux#readme",
13 | "license": "MIT",
14 | "main": "src/index.js",
15 | "types": "index.d.ts",
16 | "scripts": {
17 | "start": "node node_modules/react-native/local-cli/cli.js start",
18 | "postinstall": "./node_modules/.bin/opencollective postinstall || exit 0",
19 | "build": "./node_modules/.bin/babel src -d dist",
20 | "lint": "prettier-eslint $PWD/'src/**/*.js' $PWD/'__tests__/**/*.js' --list-different",
21 | "fix": "prettier-eslint $PWD/'src/**/*.js' $PWD/'__tests__/**/*.js' --write",
22 | "test": "jest"
23 | },
24 | "dependencies": {
25 | "@babel/runtime": "^7.6.0",
26 | "add": "^2.0.6",
27 | "lodash": "^4.17.15",
28 | "opencollective": "^1.0.3",
29 | "path-to-regexp": "^2.4.0",
30 | "prop-types": "^15.6.2",
31 | "react-navigation": "^4.x",
32 | "react-navigation-drawer": "^2.2.1",
33 | "react-navigation-stack": "^2.10.2",
34 | "react-navigation-tabs": "^2.10.1",
35 | "remove": "^0.1.5"
36 | },
37 | "devDependencies": {
38 | "@babel/cli": "7.0.0-beta.47",
39 | "@babel/code-frame": "7.0.0-beta.47",
40 | "@babel/core": "^7.6.0",
41 | "@babel/plugin-proposal-decorators": "7.0.0-beta.47",
42 | "@babel/plugin-proposal-optional-chaining": "7.0.0-beta.47",
43 | "@types/react": "16.4.12",
44 | "@types/react-native": "0.56.14",
45 | "babel-core": "^7.0.0-0",
46 | "babel-eslint": "9.0.0-beta.1",
47 | "babel-jest": "^25.1.0",
48 | "babel-preset-react-native": "5.0.2",
49 | "eslint": "^6.7.0",
50 | "eslint-config-airbnb": "17.1.0",
51 | "eslint-config-prettier": "3.0.1",
52 | "eslint-plugin-eslint-comments": "3.0.1",
53 | "eslint-plugin-import": "2.14.0",
54 | "eslint-plugin-jest": "21.22.0",
55 | "eslint-plugin-jsx-a11y": "6.1.1",
56 | "eslint-plugin-react": "7.11.0",
57 | "estraverse-fb": "1.3.2",
58 | "jest": "^25.1.0",
59 | "prettier-eslint": "8.8.2",
60 | "prettier-eslint-cli": "5.0.0-beta.0",
61 | "react": "16.4.1",
62 | "react-native": "0.56.0",
63 | "react-native-gesture-handler": "^1.4.1",
64 | "react-native-jest-mocks": "1.4.0",
65 | "react-test-renderer": "16.4.1"
66 | },
67 | "peerDependencies": {
68 | "react": "*",
69 | "react-native": "*"
70 | },
71 | "jest": {
72 | "preset": "react-native",
73 | "modulePathIgnorePatterns": [
74 | "/examples/"
75 | ],
76 | "transformIgnorePatterns": [
77 | "node_modules/(?!(jest-)?react-native|react-navigation|@react-navigation|react-navigation-(tabs|drawer|deprecated-tab-navigator|stack))"
78 | ],
79 | "setupFiles": [
80 | "./test/setup.js"
81 | ]
82 | },
83 | "eslintConfig": {
84 | "filePath": "./eslintrc.js"
85 | },
86 | "collective": {
87 | "type": "opencollective",
88 | "url": "https://opencollective.com/react-native-router-flux",
89 | "logo": "https://opencollective.com/opencollective/logo.txt"
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/examples/react-native/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 | @rem Execute Gradle
88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
89 |
90 | :end
91 | @rem End local scope for the variables with windows NT shell
92 | if "%ERRORLEVEL%"=="0" goto mainEnd
93 |
94 | :fail
95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
96 | rem the _cmd.exe /c_ return code!
97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
98 | exit /b 1
99 |
100 | :mainEnd
101 | if "%OS%"=="Windows_NT" endlocal
102 |
103 | :omega
104 |
--------------------------------------------------------------------------------
/examples/react-native/components/CustomNavBar.js:
--------------------------------------------------------------------------------
1 | import {
2 | Image,
3 | Platform,
4 | StyleSheet,
5 | Text,
6 | TouchableOpacity,
7 | View,
8 | } from 'react-native';
9 | import React from 'react';
10 | import {Actions} from 'react-native-router-flux';
11 |
12 | const styles = StyleSheet.create({
13 | container: {
14 | height: Platform.OS === 'ios' ? 64 : 54,
15 | flexDirection: 'row',
16 | paddingTop: 20,
17 | },
18 | navBarItem: {
19 | flex: 1,
20 | justifyContent: 'center',
21 | },
22 | });
23 |
24 | export default class CustomNavBar extends React.Component {
25 | // constructor(props) {
26 | // super(props)
27 | // }
28 |
29 | _renderLeft() {
30 | if (Actions.currentScene === 'customNavBar1') {
31 | return (
32 | console.log('Hamburger button pressed')}
34 | style={[styles.navBarItem, {paddingLeft: 10}]}>
35 |
43 |
44 | );
45 | }
46 | return (
47 |
50 |
55 |
56 | );
57 | }
58 |
59 | _renderMiddle() {
60 | return (
61 |
62 | {this.props.title}
63 |
64 | );
65 | }
66 |
67 | _renderRight() {
68 | return (
69 |
74 | console.log('Share')}
76 | style={{paddingRight: 10}}>
77 |
85 |
86 | console.log('Search')}
88 | style={{paddingRight: 10}}>
89 |
97 |
98 |
99 | );
100 | }
101 |
102 | render() {
103 | let dynamicStyle = {};
104 | if (Actions.currentScene === 'customNavBar1') {
105 | dynamicStyle = {backgroundColor: 'red'};
106 | } else {
107 | dynamicStyle = {backgroundColor: 'yellow'};
108 | }
109 |
110 | return (
111 |
112 | {this._renderLeft()}
113 | {this._renderMiddle()}
114 | {this._renderRight()}
115 |
116 | );
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/examples/react-native/android/app/src/debug/java/com/example/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | *
This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.example;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 |
32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
33 | client.addPlugin(new ReactFlipperPlugin());
34 | client.addPlugin(new DatabasesFlipperPlugin(context));
35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
36 | client.addPlugin(CrashReporterPlugin.getInstance());
37 |
38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
39 | NetworkingModule.setCustomClientBuilder(
40 | new NetworkingModule.CustomClientBuilder() {
41 | @Override
42 | public void apply(OkHttpClient.Builder builder) {
43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
44 | }
45 | });
46 | client.addPlugin(networkFlipperPlugin);
47 | client.start();
48 |
49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
50 | // Hence we run if after all native modules have been initialized
51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
52 | if (reactContext == null) {
53 | reactInstanceManager.addReactInstanceEventListener(
54 | new ReactInstanceManager.ReactInstanceEventListener() {
55 | @Override
56 | public void onReactContextInitialized(ReactContext reactContext) {
57 | reactInstanceManager.removeReactInstanceEventListener(this);
58 | reactContext.runOnNativeModulesQueueThread(
59 | new Runnable() {
60 | @Override
61 | public void run() {
62 | client.addPlugin(new FrescoFlipperPlugin());
63 | }
64 | });
65 | }
66 | });
67 | } else {
68 | client.addPlugin(new FrescoFlipperPlugin());
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/examples/react-native/ios/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/examples/react-native/ios/Example.xcodeproj/xcshareddata/xcschemes/Example-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/examples/react-native/components/TabView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {StyleSheet, Text, View, ViewPropTypes} from 'react-native';
4 | import Button from 'react-native-button';
5 | import {Actions} from 'react-native-router-flux';
6 |
7 | const propTypes = {
8 | name: PropTypes.string.isRequired,
9 | data: PropTypes.string,
10 | sceneStyle: ViewPropTypes.style,
11 | title: PropTypes.string.isRequired,
12 | };
13 |
14 | const defaultProps = {
15 | sceneStyle: null,
16 | };
17 |
18 | const styles = StyleSheet.create({
19 | container: {
20 | flex: 1,
21 | justifyContent: 'center',
22 | alignItems: 'center',
23 | backgroundColor: 'transparent',
24 | borderWidth: 2,
25 | borderColor: 'red',
26 | },
27 | });
28 |
29 | class TabView extends React.Component {
30 | state = {hideNavBar: false, hideTabBar: false};
31 |
32 | toggleNavBar = () => {
33 | this.setState(
34 | prevState => ({hideNavBar: !prevState.hideNavBar}),
35 | () => Actions.refresh({hideNavBar: this.state.hideNavBar}),
36 | );
37 | };
38 |
39 | toggleTabBar = () => {
40 | this.setState(
41 | prevState => ({hideTabBar: !prevState.hideTabBar}),
42 | () => {
43 | Actions.refresh(
44 | {
45 | hideTabBar: this.state.hideTabBar,
46 | },
47 | 'tab_2',
48 | );
49 | },
50 | );
51 | };
52 |
53 | render() {
54 | return (
55 |
56 |
57 | Tab title:
58 | {this.props.title} name:
59 | {this.props.name}
60 |
61 | Tab data: {this.props.data}
62 | {this.props.name === 'tab_1_1' && (
63 | Actions.tab_1_2()}>
64 | next screen for tab1_1
65 |
66 | )}
67 | {this.props.name === 'tab_2_1' && (
68 | Actions.tab_2_2()}>
69 | next screen for tab2_1
70 |
71 | )}
72 | Back
73 | {
75 | Actions.tab_1();
76 | }}>
77 | Switch to tab1
78 |
79 | {
81 | Actions.tab_2();
82 | }}>
83 | Switch to tab2
84 |
85 | {
87 | Actions.tab_3();
88 | }}>
89 | Switch to tab3
90 |
91 | {
93 | Actions.tab_4_1();
94 | }}>
95 | Switch to tab4
96 |
97 | {
99 | Actions.tab_5_1({data: 'test!'});
100 | }}>
101 | Switch to tab5 with data
102 |
103 | {
105 | Actions.echo();
106 | }}>
107 | push clone scene (EchoView)
108 |
109 | {
111 | this.toggleNavBar();
112 | }}>
113 | Toggle NavBar
114 |
115 | {
117 | Actions.replace('tab_2_1');
118 | }}>
119 | Replace with tab2
120 |
121 | {this.props.name === 'tab_2_1' && (
122 | Toggle TabBar
123 | )}
124 |
125 | );
126 | }
127 | }
128 | TabView.propTypes = propTypes;
129 | TabView.defaultProps = defaultProps;
130 |
131 | export default TabView;
132 |
--------------------------------------------------------------------------------
/examples/react-native/ios/Example/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/redux/ios/rnrfReduxSample/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/pathParser.js:
--------------------------------------------------------------------------------
1 | import pathToRegexp from 'path-to-regexp';
2 |
3 | /**
4 | *
5 | * This set of functions are used to match a url with a uri path.
6 | * This functionality is based on the internals of React-Router's matchPath.
7 | * - https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/matchPath.md
8 | *
9 | */
10 |
11 | /**
12 | * This function accepts a uri path and returns a regex to match that path
13 | * against a url and an array of the keys extracted from that uri path
14 | *
15 | * @param {String} path - a uri path in standard template (https://tools.ietf.org/html/rfc6570)
16 | *
17 | * Sample Input: "/user/:id/"
18 | *
19 | * Sample Output:
20 | * {
21 | * re: /^\/user\/([^\/]+?)\/(?:\/(?=$))?/i,
22 | * keys: [
23 | * name: "id"
24 | * delimiter: "/"
25 | * optional: false
26 | * partial: false
27 | * path: "[^\/]+?"
28 | * prefix: "/"
29 | * repeat: false,
30 | * ]
31 | * }
32 | */
33 | const compilePathToRegex = (path) => {
34 | const keys = [];
35 | const re = pathToRegexp(path, keys);
36 | // Returns the regex path to match a uri path to an actual url
37 | // and the keys that can be used to pull values from the url.
38 | return { re, keys };
39 | };
40 |
41 | /**
42 | * This function accepts a uri path and an actual url. It determines whether
43 | * or not they match one another. If they do not match, the funtion returns null.
44 | * If they do match, then the function returns the path and the params parsed
45 | * from the url.
46 | *
47 | * @param {String} path - a uri path in standard template (https://tools.ietf.org/html/rfc6570)
48 | * @param {String} url - a url that may or may not match the given path
49 | *
50 | * Case 1 - path Does Not Match Url:
51 | * Sample Input: (path: "/edit/organization/(:id)", url: "/user/300002/")
52 | *
53 | * Sample Output: null
54 | *
55 | * Case 2 - path Does Match Url:
56 | * Sample Input: (path: "/user/:id/", url: "/user/300002/")
57 | *
58 | * Sample Output:
59 | * {
60 | * path: "/user/:id/",
61 | * params: {
62 | * id: "300002",
63 | * }
64 | * }
65 | *
66 | */
67 | export const matchPath = (path, url) => {
68 | // Remove possible query fragments, which are not supported by iOS and some
69 | // versions of Anroid.
70 | const [urlCleaned] = url.split('?');
71 |
72 | // Append trailing slash for compatibility with pathToRegexp
73 | const urlToMatch = !urlCleaned.endsWith('/') ? `${urlCleaned}/` : urlCleaned;
74 |
75 | // Return the regex and the keys that can be parsed from a uri path.
76 | const { re, keys } = compilePathToRegex(path);
77 |
78 | // Check if the given url matches the uri path.
79 | const match = re.exec(urlToMatch);
80 |
81 | // If there is no match, then return null.
82 | if (!match) {
83 | return null;
84 | }
85 |
86 | // Destructure to return the compiled url (aka the reconstructed url based
87 | // on the regex and the url parameters.
88 | const [compiledUrl, ...values] = match;
89 |
90 | // If there is an inexact match (aka the compiled path does not match the
91 | // given url, then return null)
92 | if (urlToMatch !== compiledUrl) {
93 | return null;
94 | }
95 |
96 | const params = keys.reduce((acc, key, index) => Object.assign({}, acc, { [key.name]: values[index] }), {});
97 |
98 | return { path, params };
99 | };
100 |
101 | /**
102 | * This function accepts an array of uri paths and a url. If there are no paths
103 | * in the array that match the given url, then the function will return null.
104 | * If there is at least one matching uri path, it will return the first
105 | * matching path and the parsed url parameters (the output from matchPath()).
106 | *
107 | * @param {Array} possibleMatchingpaths - an array of uri paths in standard template (https://tools.ietf.org/html/rfc6570)
108 | * @param {String} url - a url that may or may not match a given path
109 | *
110 | */
111 | const pathParser = (url, possibleMatchingpaths = []) => possibleMatchingpaths.map(path => matchPath(path, url)).find(obj => obj);
112 |
113 | export default pathParser;
114 |
--------------------------------------------------------------------------------
/examples/expo/navigation/AppNavigator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Platform, StyleSheet, Text, View } from 'react-native';
3 | import { StackViewStyleInterpolator } from 'react-navigation-stack';
4 | import {
5 | Scene,
6 | Router,
7 | Actions,
8 | Reducer,
9 | ActionConst,
10 | Overlay,
11 | Tabs,
12 | Modal,
13 | Drawer,
14 | Stack,
15 | Lightbox,
16 | } from 'react-native-router-flux';
17 | import TabBarIcon from '../components/TabBarIcon';
18 | import MenuIcon from '../components/MenuIcon';
19 | import DrawerContent from '../components/DrawerContent';
20 | import HomeScreen from '../screens/HomeScreen';
21 | import LinksScreen from '../screens/LinksScreen';
22 | import SettingsScreen from '../screens/SettingsScreen';
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | flex: 1,
27 | backgroundColor: 'transparent',
28 | justifyContent: 'center',
29 | alignItems: 'center',
30 | },
31 | tabBarStyle: {
32 | backgroundColor: '#eee',
33 | },
34 | tabBarSelectedItemStyle: {
35 | backgroundColor: '#ddd',
36 | },
37 | });
38 |
39 | const reducerCreate = params => {
40 | const defaultReducer = new Reducer(params);
41 | return (state, action) => {
42 | console.log('reducer: ACTION:', action);
43 | return defaultReducer(state, action);
44 | };
45 | };
46 |
47 | const stateHandler = (prevState, newState, action) => {
48 | console.log('onStateChange: ACTION:', action);
49 | };
50 |
51 | const getSceneStyle = () => ({
52 | backgroundColor: '#F5FCFF',
53 | shadowOpacity: 1,
54 | shadowRadius: 3,
55 | });
56 |
57 | // on Android, the URI prefix typically contains a host in addition to scheme
58 | const prefix = Platform.OS === 'android' ? 'mychat://mychat/' : 'mychat://';
59 |
60 | const transitionConfig = () => ({
61 | screenInterpolator:
62 | StackViewStyleInterpolator.forFadeFromBottomAndroid,
63 | });
64 |
65 | const AppNavigator = () => (
66 |
71 |
72 |
73 |
74 |
75 |
76 | {
80 | console.log('Drawer closed');
81 | }}
82 | onEnter={() => {
83 | console.log('Drawer opened');
84 | }}
85 | contentComponent={DrawerContent}
86 | drawerIcon={MenuIcon}
87 | drawerWidth={300}>
88 |
89 | {
93 | console.log('Back to initial and also print this');
94 | }}
95 | swipeEnabled
96 | tabBarStyle={styles.tabBarStyle}
97 | activeBackgroundColor="white"
98 | inactiveBackgroundColor="rgba(255, 0, 0, 0.5)">
99 |
106 |
113 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | );
129 |
130 | export default AppNavigator;
131 |
--------------------------------------------------------------------------------
/docs/v3/MINI_TUTORIAL.md:
--------------------------------------------------------------------------------
1 | # Mini-Tutorial
2 |
3 | 
4 |
5 | In this super simple example, we will just have three files:
6 |
7 | 1. Your root index file: `index.js`
8 | 2. The first page that is loaded automatically: `PageOne.js`
9 | 3. A second page you can navigate to: `PageTwo.js`
10 |
11 | ### index.js
12 | ```jsx
13 | import React, { Component } from 'react';
14 | import { Router, Scene } from 'react-native-router-flux';
15 |
16 | import PageOne from './PageOne';
17 | import PageTwo from './PageTwo';
18 |
19 | export default class App extends Component {
20 | render() {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 | }
31 | ```
32 |
33 | In `react-native-router-flux`, each route (or page) is called a ``. Conventionally, your Scenes should be wrapped inside a root Scene before being finally wrapped inside a `` component that is returned in the `render()` function.
34 |
35 | At the very minimum, each `` component should have the following props:
36 |
37 | - **key**: A unique string that can be used to refer to the particular scene.
38 | - **component**: The component to be rendered for that `Scene` or page.
39 | - **title**: The string to be displayed in the nav bar at the top of the screen.
40 |
41 | Note that the first scene we wish to load has the prop `initial={true}` to indicate that it's the scene that should be initially rendered.
42 |
43 | ## From Page to Page
44 |
45 | ### PageOne.js
46 | ```jsx
47 | import React, { Component } from 'react';
48 | import { View, Text } from 'react-native';
49 | import { Actions } from 'react-native-router-flux';
50 |
51 | export default class PageOne extends Component {
52 | render() {
53 | return (
54 |
55 | This is PageOne!
56 |
57 | )
58 | }
59 | }
60 | ```
61 |
62 | To navigate from one route to another, an `Action` must be called. This takes the form of:
63 |
64 | ```
65 | Actions.SCENE_KEY(PARAMS)
66 | ```
67 |
68 | Where `SCENE_KEY` must match the `key` prop defined in one of the Scenes of the `Router` component in the root file. And `PARAMS` refers to a javascript object that will be passed into the resulting scene as props (more on this later).
69 |
70 | Since the PageTwo component has the key of `pageTwo`, all we need to do is to pass in the function `Actions.pageTwo` into the `` component so that it executes the page transition when the text is pressed.
71 |
72 | ## Passing Information
73 |
74 | Now let's try to extend our example so that we can pass data from `PageOne` to `PageTwo`.
75 |
76 | In `PageOne.js`, instead of simply passing in `Actions.pageTwo`, we can replace it with `Actions.pageTwo({text: 'Hello World!'})`. In this case, we need to wrap the Action call inside a function to prevent it from executing when this component is rendered. As a result, the render function inside `PageOne.js` should look like this:
77 |
78 | ```jsx
79 | render() {
80 | const goToPageTwo = () => Actions.pageTwo({text: 'Hello World!'});
81 | return (
82 |
83 | This is PageOne!
84 |
85 | )
86 | }
87 | ```
88 |
89 | And in `PageTwo.js`, we can use the data being passed in by adding an additional `` component below the existing one like so:
90 |
91 | ```jsx
92 | render() {
93 | return (
94 |
95 | This is PageTwo!
96 | {this.props.text}
97 |
98 | )
99 | }
100 | ```
101 |
102 | Now, if we navigate to the PageTwo Scene as before, we should see:
103 |
104 | ```
105 | This is PageTwo!
106 | Hello World!
107 | ```
108 |
109 | ## Going Forward (or backwards?)
110 |
111 | That pretty much concludes this mini-tutorial, we've covered most of the basics here. There are a lot of things you can do to customize `react-native-router-flux`, this is only the beginning.
112 |
113 | For example, you may want to programmatically go back to the previous Scene. The included navbar already allows you to do this by pressing on the arrow icon at the upper left corner. But you can also call this function at any point in your app for the same effect:
114 |
115 | ```js
116 | Actions.pop()
117 | ```
118 |
119 | And should you ever want to refresh the same Scene with new props, you can use:
120 |
121 | ```js
122 | Actions.refresh(PARAMS)
123 | ```
124 |
125 | Don't be afraid to explore the docs, you'll be surprised at how much you're able to customize with `react-native-router-flux`!
126 |
--------------------------------------------------------------------------------
/docs/v3/DETAILED_EXAMPLE.md:
--------------------------------------------------------------------------------
1 | ## Detailed Example
2 |
3 | for latest example code, please see [Example](https://github.com/aksonov/react-native-router-flux/blob/master/Example/Example.js)
4 |
5 | 
6 |
7 | ```jsx
8 | import React, {AppRegistry, Navigator, StyleSheet, Text, View} from 'react-native'
9 | import Launch from './components/Launch'
10 | import Register from './components/Register'
11 | import Login from './components/Login'
12 | import Login2 from './components/Login2'
13 | import { Scene, Router, TabBar, Modal, Schema, Actions, Reducer, ActionConst } from 'react-native-router-flux'
14 | import Error from './components/Error'
15 | import Home from './components/Home'
16 | import TabView from './components/TabView'
17 |
18 | class TabIcon extends React.Component {
19 | render(){
20 | return (
21 | {this.props.title}
22 | );
23 | }
24 | }
25 |
26 | const reducerCreate = params=>{
27 | const defaultReducer = Reducer(params);
28 | return (state, action)=>{
29 | console.log("ACTION:", action);
30 | return defaultReducer(state, action);
31 | }
32 | };
33 |
34 | export default class Example extends React.Component {
35 | render() {
36 | return
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | alert("Right button")} rightTitle="Right" />
50 |
51 |
52 |
53 | alert("Left button!")} leftTitle="Left"/>
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | ;
64 | }
65 | }
66 |
67 | ```
68 |
69 | components/Launch.js (initial screen)
70 |
71 | ```jsx
72 | import React, {View, Text, StyleSheet, TouchableHighlight} from 'react-native'
73 | import Button from 'react-native-button'
74 | import {Actions} from 'react-native-router-flux'
75 |
76 | class Launch extends React.Component {
77 | render(){
78 | return (
79 |
80 | Launch page
81 | Actions.login({data:"Custom data", title:'Custom title' })}>Go to Login page
82 | Go to Register page
83 | Go to Register page without animation
84 | Show error popup
85 | Go to TabBar page
86 |
87 | );
88 | }
89 | }
90 |
91 | var styles = StyleSheet.create({
92 | container: {
93 | flex: 1,
94 | justifyContent: 'center',
95 | alignItems: 'center',
96 | backgroundColor: 'transparent',
97 | }
98 | });
99 |
100 | module.exports = Launch;
101 | ```
102 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribute
2 |
3 | ## Introduction
4 |
5 | First, thank you for considering contributing to react-native-router-flux! It's people like you that make the open source community such a great community! 😊
6 |
7 | We welcome any type of contribution, not only code. You can help with
8 | - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
9 | - **Marketing**: writing blog posts, howto's, printing stickers, ...
10 | - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
11 | - **Code**: take a look at the [open issues](https://github.com/aksonov/react-native-router-flux/issues?q=is%3Aopen+is%3Aissue). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. To get started you can also [sign up to triage react-native-router-flux issues on CodeTriage](https://www.codetriage.com/aksonov/react-native-router-flux).
12 | - **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-router-flux).
13 |
14 | ## Your First Contribution
15 |
16 | Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
17 |
18 | ## Submitting code
19 |
20 | Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.
21 |
22 | ## Code review process
23 |
24 | The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
25 | It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?
26 |
27 | ## Financial contributions
28 |
29 | We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-router-flux).
30 | Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
31 |
32 | ## Questions
33 |
34 | If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!).
35 | You can also reach us at hello@react-native-router-flux.opencollective.com.
36 |
37 | ## Credits
38 |
39 | ### Contributors
40 |
41 | Thank you to all the people who have already contributed to react-native-router-flux!
42 |
43 |
44 |
45 | ### Backers
46 |
47 | Thank you to all our backers! [[Become a backer](https://opencollective.com/react-native-router-flux#backer)]
48 |
49 |
50 |
51 |
52 | ### Sponsors
53 |
54 | Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/react-native-router-flux#sponsor))
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------