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); } }
コメント
コメントを投稿