react-native/reduxでのフロントエンドDDD
react-native/reduxでのフロントエンドDDD:
下記を使用
- react-native
- redux
- typescript
- immutable.js
- firebase
構成図
パッケージ構成
|--applications | |--App.tsx | |--store.tsx |--assets |--domains | |--navigation-domain | | |--nav-container.tsx | | |--nav-model.tsx | | |--nav-reducer.tsx | |--profile-domain | | |--profile-action.tsx | | |--profile-model.tsx | | |--profile-reducer.tsx | | |--profile-thunk.tsx |--views | |--atoms | | |--sample-atom.tsx | |--molecules | | |--sample-mol.tsx | |--organisms | | |--sample-org.tsx | |--pages | | |--sample-page.tsx | |--templates | | |--footer-tmp.tsx | | |--header-tmp.tsx
アーキテクチャ構成
react-component
- atomic designで構成します。
- page, organism, template, molecule, atomの構成となります。
- 上層から下層の呼び出しのみを許可します。
- pageはorganism、templateを呼び出すのみ。
- organismはmoleculeを呼び出すのみ。
- moleculeはatomを呼び出すのみ。
- 描写のみを担当して、ビジネスロジックは持ちません。
profile-page.tsx
/** プロフィールpage */
export default class ProfilePage extends Component {
render() {
return (
<Container>
<HeaderTmp/>
<Content>
<ProfileInputOrg/>
</Content>
<FooterTmp/>
</Container>
);
}
}
profile-input-org.tsx
interface Props {
userId: string,
profileModel: ProfileModel,
profileFind: Function,
profileUpdate: Function,
textChange: Function,
}
/** プロフィールorganism */
class ProfileInputOrg extends Component<Props>{
componentWillMount() {
this.props.profileFind(this.props.userId);
}
render() {
return (
<View style={styles.inputPanel}>
<Form>
<Item floatingLabel last>
<Label>Self Introduction</Label>
<Input
style={styles.inputArea}
multiline={true}
numberOfLines={5}
onChangeText={(value) => this.props.textChange(value)}
value={this.props.profileModel.getText()}
/>
</Item>
</Form>
<View style={styles.buttonPanel}>
<Button style={styles.button}
rounded
onPress={() => this.props.profileUpdate(this.props.userId, this.props.profileModel)}
>
<Icon type="FontAwesome" name="save"/>
</Button>
</View>
<ImageUploadOrg/>
</View>
)
}
}
const mapStateToProps = (state: any) => ({
userId: state.LoginReducer.getUserId(),
profileModel: state.ProfileReducer,
});
const mapDispatchToProps = (dispatch: any) => {
return {
// プロフィール取得
profileFind(userId: string) {
dispatch(profileFind(userId))
},
// プロフィール更新
profileUpdate(userId: string, profileModel: ProfileModel) {
dispatch(profileUpdate(userId, profileModel));
},
// テキスト変更
textChange(value: string) {
dispatch(textChange(value))
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ProfileInputOrg)
action
- 各page, organism, molecule, atomにてdispatchします。
- api通信が必要のないものに限られます。
profile-action.tsx
export const TEXT_CHANGE = "TEXT_CHANGE";
/** テキストの変更時のアクションです */
export const textChange = (value: string) => {
return {
type: TEXT_CHANGE,
value: value
}
}
thunks
- api通信が必要な場合はthunksにて実装します。
- serverからのレスポンスを待ってからreducerでの処理を行います。
profile-thunks.tsx
export const PROFILE_FIND = "PROFILE_FIND";
/** FireDb(例)のプロフィールを取得します */
export const profileFind = (userId: string) => {
return (dispatch: any) => {
fireDb
.ref(`profile/-${userId}`)
.on('value', (snapshot: any) => {
dispatch({ type: PROFILE_FIND, payload: snapshot.val()});
})
};
}
reducer
- 各ドメイン毎にreducerが存在します。
- actionやthunksによりどの処理を振り分けます。
- componentからactionを介して受け取ったmodel、thunksを介して受け取ったapi のresponse を元に
新しいdomain modelをcomponentに返却します。
profile-reducer.tsx
/** プロフィール用のreducer */
export const ProfileReducer = (profileModel = new ProfileModel(), action: any) => {
switch (action.type) {
// テキスト変更時
case TEXT_CHANGE:
return profileModel.changeText(action.value);
// プロフィール新規作成時
case PROFILE_CREATE:
return profileModel.create(action.user);
domain model
- immutableなmodelとします。
profile-model.tsx
import {Record} from "immutable";
/** プロフィール用のモデル */
export default class ProfileModel extends Record({
/** 名前 */
name: '',
/** 住所 */
address: '',
/** 誕生日 */
birthday: '',
/** 紹介文 */
text: '',
}) {
public getName(): string {
return this.get('name');
}
public getAddress(): string {
return this.get('address');
}
public getBirthday(): string {
return this.get('birthday');
}
public getText(): string {
return this.get('text');
}
/** テキスト変更 */
public changeText(text: string): ProfileModel {
return this.set('text', text);
}
/** 新規作成 */
public create(text: string): ProfileModel {
return this.set('name', text)
.set('address', text)
.set('birthday', text);
}
}
コメント
コメントを投稿