既存のプロジェクトであれこれ試す時は、シンプルなプロジェクトで試したい
それに、アプリをつくっていると、だんだん適当になるので、、適度に挙動を確認
3つの画面を react-native-router-flux で遷移しつつ、ライフサイクルがどのような動いてるかを確認するようのテンプレート
1つのStoreを持っていて、
- A (storeの表示)
- B (storeの更新1)
- C (storeの更新2)
となるように動く。イメージとしてはプロフィールの表示、編集、編集完了という感じ。
storeの更新、ページの遷移時の引数、などなどで混乱ないように
reduxは、redux-aggregateを使うのが最近のお気に入り。
###とりあえずアプリ作成###
react-native init sampleApp
###環境###
生成された package.json
package.json
{
"name": "sampleApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.3.1",
"react-native": "0.55.4"
},
"devDependencies": {
"babel-jest": "23.0.1",
"babel-preset-react-native": "4.0.0",
"jest": "23.1.0",
"react-test-renderer": "16.3.1"
},
"jest": {
"preset": "react-native"
}
}
###必要なパッケージを足す###
yarn add react-native-router-flux react-redux redux redux-aggregate redux-devtools-extension
##ベースとなるアプリ##
Qiitaの仕様で.jsxにしてるけど、ほんとは.js
app/index.jsx
import React, { Component } from 'react';
import { Router, Scene } from 'react-native-router-flux';
import Ascreen from './containers/Ascreen';
import Bscreen from './containers/Bscreen';
import Cscreen from './containers/Cscreen';
class App extends Component {
render() {
return (
<Router>
<Scene key="navigation" hideNavBar={false}>
<Scene key="ascreen" component={Ascreen} />
<Scene key="bscreen" component={Bscreen} />
<Scene key="cscreen" component={Cscreen} />
</Scene>
</Router>
);
}
}
export default App;
app/countainers/Ascreen.jsx
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Actions } from 'react-native-router-flux';
class App extends Component {
render() {
return (
<View style={styles.container}>
<Text>Screen A</Text>
<Button
onPress={() => {
Actions.bscreen();
}}
title="NEXT"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default App;
app/countainers/Bscreen.jsx
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Actions } from 'react-native-router-flux';
class BScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>Screen B</Text>
<Button
onPress={() => {
Actions.cscreen();
}}
title="NEXT C"
/>
<Button
onPress={() => {
Actions.pop();
}}
title="BACK A"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default BScreen;
app/countainers/Cscreen.jsx
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Actions } from 'react-native-router-flux';
class CScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>Screen C</Text>
<Button
onPress={() => {
Actions.pop();
}}
title="BACK B"
/>
<Button
onPress={() => {
Actions.popTo('ascreen');
}}
title="BACK A"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default CScreen;
##store追加##
追加するとこんな感じ
app/index.jsx
import React, { Component } from 'react';
import { Router, Scene } from 'react-native-router-flux';
import { Provider } from 'react-redux';
import { store } from './store';
import Ascreen from './containers/Ascreen';
import Bscreen from './containers/Bscreen';
import Cscreen from './containers/Cscreen';
class App extends Component {
render() {
return (
<Provider store={store}>
<Router>
<Scene key="navigation" hideNavBar={false}>
<Scene key="ascreen" component={Ascreen} />
<Scene key="bscreen" component={Bscreen} />
<Scene key="cscreen" component={Cscreen} />
</Scene>
</Router>
</Provider>
);
}
}
export default App;
app/store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { createAggregate } from 'redux-aggregate';
import { composeWithDevTools } from 'redux-devtools-extension';
import { ProfileST, ProfileMT } from './models/profile';
const middleware = [];
export function defineStore(reducer) {
return createStore(
combineReducers(reducer),
composeWithDevTools(applyMiddleware(...middleware))
);
}
export const Profile = createAggregate(ProfileMT, 'profile/');
export const store = defineStore({
profile: Profile.reducerFactory({ ...ProfileST, name: 'PROFILE' }),
});
app/models/profile.js
export const ProfileST = {
uid: '',
};
// @ Queries
function getUid(state) {
return `uid:${state.uid}`;
}
export const ProfileQR = {
getUid,
};
// @ Mutations
function updateUid(state, payload) {
return { ...state, uid: payload };
}
function resetUid(state) {
return { ...state, uid: '' };
}
export const ProfileMT = {
updateUid,
resetUid,
};
app/countainers/Ascreen.jsx
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Actions } from 'react-native-router-flux';
import { connect } from 'react-redux';
import { Profile } from '../store';
import { ProfileQR } from '../models/profile';
class AScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>Screen A</Text>
<Text>{this.props.userId}</Text>
<Button
onPress={() => {
this.props.updateUid('Taro');
Actions.bscreen();
}}
title="NEXT"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default connect(
({ profile }) => ({ profile, userId: ProfileQR.getUid(profile) }),
{
...Profile.creators,
}
)(AScreen);