diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..8df53fe --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ +"presets": ["react-native"] +} \ No newline at end of file diff --git a/.flowconfig b/.flowconfig index 168c3c6..3b261e2 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,7 +1,7 @@ [ignore] # We fork some components by platform. -.*/*.android.js +.*/*[.]android.js # Ignore templates with `@flow` in header .*/local-cli/generator.* @@ -48,11 +48,11 @@ suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-9]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-9]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-3]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-3]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy unsafe.enable_getters_and_setters=true [version] -^0.29.0 +^0.33.0 diff --git a/__tests__/example-test.js b/__tests__/example-test.js new file mode 100644 index 0000000..e09de39 --- /dev/null +++ b/__tests__/example-test.js @@ -0,0 +1,6 @@ +import example from '../src/models/example'; + +test('it should save', () => { + expect(example.reducers['example/save']({}, { payload: { a: 1 }})) + .toEqual({ a: 1 }); +}); diff --git a/index.android.js b/index.android.js index be175e3..d7a5e75 100644 --- a/index.android.js +++ b/index.android.js @@ -1,53 +1 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - * @flow - */ - -import React, { Component } from 'react'; -import { - AppRegistry, - StyleSheet, - Text, - View -} from 'react-native'; - -class DvaExampleReactNative extends Component { - render() { - return ( - - - Welcome to React Native! - - - To get started, edit index.android.js - - - Double tap R on your keyboard to reload,{'\n'} - Shake or press menu button for dev menu - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#F5FCFF', - }, - welcome: { - fontSize: 20, - textAlign: 'center', - margin: 10, - }, - instructions: { - textAlign: 'center', - color: '#333333', - marginBottom: 5, - }, -}); - -AppRegistry.registerComponent('DvaExampleReactNative', () => DvaExampleReactNative); +import './src'; diff --git a/index.ios.js b/index.ios.js index bd97e32..d7a5e75 100644 --- a/index.ios.js +++ b/index.ios.js @@ -1,73 +1 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - * @flow - */ - -import React, { Component } from 'react'; -import { - AppRegistry, - StyleSheet, - Text, - View, - Button, - TouchableHighlight, -} from 'react-native'; - -import dva, { connect } from 'dva/mobile'; - -function delay(timeout) { - return new Promise(resolve => { - setTimeout(resolve, timeout); - }); -} - -const app = dva(); -app.model({ - namespace: 'count', - state: 0, - reducers: { - add(state) { return state + 1 }, - }, - effects: { - *addDelay(action, { call, put }) { - yield call(delay, 1000); - yield put({ type: 'add' }); - }, - }, - subscriptions: { - setup({ dispatch }) { - dispatch({type: 'add'}); - }, - }, -}); - -const App = connect(({ count }) => ({ count }))((props) => { - const { dispatch, count } = props; - return ( - - - Count: { count } - - { dispatch({ type: 'count/add' }) }}> - Add - - { dispatch({ type: 'count/addDelay' }) }}> - Delay Add - - - ); -}); - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#F5FCFF', - }, -}); - -app.router(() => ); - -AppRegistry.registerComponent('DvaExampleReactNative', () => app.start()); +import './src'; diff --git a/package.json b/package.json index 3208db4..4ebdd55 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,23 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "node node_modules/react-native/local-cli/cli.js start" + "start": "node node_modules/react-native/local-cli/cli.js start", + "test": "jest" }, "dependencies": { "dva": "1.1.0", "react": "15.3.2", "react-dom": "15.3.2", "react-native": "0.36.0" + }, + "jest": { + "preset": "jest-react-native" + }, + "devDependencies": { + "babel-jest": "16.0.0", + "babel-preset-react-native": "1.9.0", + "jest": "16.0.2", + "jest-react-native": "16.1.0", + "react-test-renderer": "15.3.2" } } diff --git a/src/components/CounterText.js b/src/components/CounterText.js new file mode 100644 index 0000000..cc88b2c --- /dev/null +++ b/src/components/CounterText.js @@ -0,0 +1,18 @@ +import React, { Component } from 'react'; +import { + Text +} from 'react-native'; + +var CounterText = React.createClass({ + + render: function() { + return ( + + Count: { this.props.count } + + ); + } + +}); + +module.exports = CounterText; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..26b933b --- /dev/null +++ b/src/index.js @@ -0,0 +1,17 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * @flow + */ +import React, { Component } from 'react'; +import { + AppRegistry +} from 'react-native'; +import dva from 'dva/mobile'; +import Counter from './pages/Counter'; + +const app = dva(); +app.model(require('./models/count.js')); +app.router(() => ); + +AppRegistry.registerComponent('DvaExampleReactNative', () => app.start()); diff --git a/src/models/count.js b/src/models/count.js new file mode 100644 index 0000000..eab6a03 --- /dev/null +++ b/src/models/count.js @@ -0,0 +1,23 @@ +function delay(timeout) { + return new Promise(resolve => { + setTimeout(resolve, timeout); + }); +} +module.exports = { + namespace: 'count', + state: 0, + reducers: { + add(state) { return state + 1 }, + }, + effects: { + *addDelay(action, { call, put }) { + yield call(delay, 1000); + yield put({ type: 'add' }); + }, + }, + subscriptions: { + setup({ dispatch }) { + dispatch({type: 'add'}); + }, + }, +}; diff --git a/src/models/example.js b/src/models/example.js new file mode 100644 index 0000000..c29639c --- /dev/null +++ b/src/models/example.js @@ -0,0 +1,27 @@ + +export default { + + namespace: 'example', + + state: {}, + + subscriptions: { + setup({ dispatch, history }) { + }, + }, + + effects: { + *fetchRemote({ payload }, { call, put }) { + }, + }, + + reducers: { + fetch(state, action) { + return { ...state, ...action.payload }; + }, + 'example/save'(state, action) { + return { ...action.payload }; + }, + }, + +} diff --git a/src/pages/Counter.js b/src/pages/Counter.js new file mode 100644 index 0000000..6a4500b --- /dev/null +++ b/src/pages/Counter.js @@ -0,0 +1,36 @@ +import React, { Component } from 'react'; +import { + StyleSheet, + Text, + View, + Button, + TouchableOpacity, +} from 'react-native'; +import { connect } from 'dva/mobile'; +import CounterText from '../components/CounterText'; + +const App = connect(({ count }) => ({ count }))((props) => { + const { dispatch, count } = props; + return ( + + + { dispatch({ type: 'count/add' }) }}> + Add + + { dispatch({ type: 'count/addDelay' }) }}> + Delay Add + + + ); +}); + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#F5FCFF', + }, +}); + +export default App; diff --git a/src/services/example.js b/src/services/example.js new file mode 100644 index 0000000..bbf54e3 --- /dev/null +++ b/src/services/example.js @@ -0,0 +1,5 @@ +import request from '../utils/request'; + +export async function query() { + return request('/api/users'); +} diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 0000000..c808422 --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,30 @@ +import fetch from 'dva/fetch'; + +function parseJSON(response) { + return response.json(); +} + +function checkStatus(response) { + if (response.status >= 200 && response.status < 300) { + return response; + } + + const error = new Error(response.statusText); + error.response = response; + throw error; +} + +/** + * Requests a URL, returning a promise. + * + * @param {string} url The URL we want to request + * @param {object} [options] The options we want to pass to "fetch" + * @return {object} An object containing either "data" or "err" + */ +export default function request(url, options) { + return fetch(url, options) + .then(checkStatus) + .then(parseJSON) + .then((data) => ({ data })) + .catch((err) => ({ err })); +}