-
Notifications
You must be signed in to change notification settings - Fork 17
Step4:フォームで入力した値をAPIに送信する
redux-pluto では入力フォームの制御やバリデーションに redux-form を使用してます。
redux-form を使用して入力フォームを作成し、入力した値をAPIに送信してみましょう。
まずは form で入力した情報の送信先であるところの API 定義から作成していきましょう。
path が '/hello' method が post のAPIを追加しましょう。
spec/agreed/hello/postComment.ts
+import { APIDef, POST, Success201, ResponseDef } from "agreed-typed";
+
+export type PostCommentAPI = APIDef<
+ POST,
+ ["hello"],
+ {}, // header
+ {}, // query
+ { text: string }, // request body
+ {}, // response header
+ ResponseDef<Success201, { text: string }>
+>;
+
+const api: PostCommentAPI = {
+ request: {
+ path: ["hello"],
+ method: "POST",
+ body: {
+ text: "{:text}",
+ },
+ values: {
+ text: "hello",
+ },
+ },
+ response: {
+ status: 201,
+ body: {
+ id: "0003",
+ text: "hello",
+ }
+ },
+};
+
+module.exports = api;
次に定義したファイルを agreed.ts ファイルに追加し、export させます。
spec/agreed/agreed.ts
...flatten([
require("./agreedsample/get"),
require("./uploadsample/post"),
require("./hello/getComments"),
+ require("./hello/postComment"),
]),
);
~~~
import Axios from "axios";
-import { read } from "./utils"; // axios を使い get request を投げる util
+import { read, create } from "./utils"; // axios を使い get request を投げる util
export default class Hello {
name: string;
~~~
read(req: any, resource: any, params: any = {}, config: any) {
return read(this.axios, this.name, this.pathname, params, {});
}
+
+ create(req: any, resource: any, params: any, body?: any, config?: any) {
+ return create(this.axios, this.name, this.pathname, body, params, {});
+ }
}
step4 と同様に リクエストを飛ばす処理を作成するので、
リクエスト送信・成功・失敗それぞれの Action type, ActionCreater, Reducer を作成します。
src/shared/redux/modules/hello.ts
~~~
import { steps } from "redux-effects-steps";
-import { fetchrRead } from "redux-effects-fetchr";
+import { fetchrRead, fetchrCreate } from "redux-effects-fetchr";
/**
* Action types
~~~
const HELLO_GET_COMMENTS_REQUEST = "redux-pluto/hello/get/comments/request";
const HELLO_GET_COMMENTS_SUCCESS = "redux-pluto/hello/get/comments/success";
const HELLO_GET_COMMENTS_FAIL = "redux-pluto/hello/get/comments/fail";
+const HELLO_POST_COMMENT_REQUEST = "redux-pluto/hello/post/comments/request";
+const HELLO_POST_COMMENT_SUCCESS = "redux-pluto/hello/post/comments/success";
+const HELLO_POST_COMMENT_FAIL = "redux-pluto/hello/post/comments/fail";
type ChangeVisibility = {
type: typeof HELLO_CHANGE_VISIBILITY;
~~~
type: typeof HELLO_GET_COMMENTS_FAIL;
error: boolean;
};
-
+type PostCommentRequest = {
+ type: typeof HELLO_POST_COMMENT_REQUEST;
+ payload: {
+ resource: string;
+ body: {
+ text: string;
+ };
+ };
+};
+type PostCommentSuccess = {
+ type: typeof HELLO_POST_COMMENT_SUCCESS;
+ payload: {
+ data: {
+ id: string;
+ text: string;
+ };
+ };
+};
+type PostCommentFail = {
+ type: typeof HELLO_POST_COMMENT_FAIL;
+ error: boolean;
+};
type Action =
| ChangeVisibility
| CommentsRequest
| CommentsSuccess
- | CommentsFail;
+ | CommentsFail
+ | PostCommentRequest
+ | PostCommentSuccess
+ | PostCommentFail;
/**
* Action creators
~~~
);
}
+export function postCommentRequest(payload: {
+ resource: string;
+ body: { text: string };
+}): PostCommentRequest {
+ return {
+ type: HELLO_POST_COMMENT_REQUEST,
+ payload,
+ };
+}
+
+export function postCommentSuccess(payload: {
+ data: { id: string; text: string };
+}): PostCommentSuccess {
+ return {
+ type: HELLO_POST_COMMENT_SUCCESS,
+ payload,
+ };
+}
+
+export function postCommentFail(): PostCommentFail {
+ return {
+ type: HELLO_POST_COMMENT_FAIL,
+ error: true,
+ };
+}
+
+export function postComment(body: { text: string }) {
+ return steps(
+ postCommentRequest({ resource: "hello", body }),
+ ({ payload }) => fetchrCreate(payload),
+ [postCommentSuccess, postCommentFail],
+ );
+}
+
/**
* Initial state
*/
~~~
loaded: false,
};
}
+ case HELLO_POST_COMMENT_REQUEST: {
+ return {
+ ...state,
+ loading: true,
+ loaded: false,
+ };
+ }
+ case HELLO_POST_COMMENT_SUCCESS: {
+ const {
+ data: { id, text },
+ } = action.payload;
+ return {
+ ...state,
+ comments: [...state.comments, { id, text }],
+ loading: false,
+ loaded: true,
+ };
+ }
+ case HELLO_POST_COMMENT_FAIL: {
+ const { error } = action;
+ return {
+ ...state,
+ error,
+ loading: false,
+ loaded: false,
+ };
+ }
default: {
return state;
}
reducer の準備が整ったので、次は画面上で値を入力するフォームを作成していきます
organisms/Hello/index.ts
~~~
import { compose } from "redux";
import { connect } from "react-redux";
import { asyncLoader } from "redux-async-loader"; // redux-asynch-roder から asyncLoader をインポート
+import { reduxForm } from "redux-form";
import { changeVisibility, getComments } from "../../../redux/modules/hello";
import { RootState } from "../../../redux/modules/reducer";
import Hello from "./Hello";
~~~
onChangeVisibility: () => dispatch(changeVisibility()), // changeVisibilityを store に dispatchする関数を返す
}),
),
+ reduxForm({
+ form: "hello",
+ }),
)(Hello);
organisms/Hello/Hello.tsx
~~~
import React from "react";
+import { Field } from "redux-form";
export type Props = {
// props の型定義
~~~
<button type="button" onClick={() => onChangeVisibility()}>
{isVisible ? "hide" : "show"}
</button>
+ <div>
+ <Field name="text" component="input" type="text" />
+ </div>
</div>
);
}
store を見ると、form.hello としてフォームが追加されており、
テキストフィールドに入力することでその値が store に反映されている事がわかります。
Redux Form から値を取得するために、form タグ を追加し、入力値を handleSubmit で処理するように変更します。
Container component
reduxForm 内に handleSubmit が発火した際に form の値を Store に dispatch するonSubmit(values, dispatch)関数を定義します。
第1引数の values には form 内に定義した field の values オブジェクトが入ります。
organisms/Hello/index.ts
~~~
import { connect } from "react-redux";
import { asyncLoader } from "redux-async-loader"; // redux-asynch-roder から asyncLoader をインポート
import { reduxForm } from "redux-form";
-import { changeVisibility, getComments } from "../../../redux/modules/hello";
+import {
+ changeVisibility,
+ getComments,
+ postComment,
+} from "../../../redux/modules/hello";
import { RootState } from "../../../redux/modules/reducer";
import Hello from "./Hello";
~~~
),
reduxForm({
form: "hello",
+ onSubmit(values: { text: string }, dispatch: any) {
+ dispatch(postComment(values));
+ },
}),
)(Hello);
Presentational component
form と submit 用の button を追加します。
organisms/Hello/Hello.js
~~~
};
export default function Hello(props: Props) {
- const { isVisible, onChangeVisibility, comments } = props;
+ const { isVisible, onChangeVisibility, comments, handleSubmit } = props;
return (
<div>
{isVisible &&
~~~
<button type="button" onClick={() => onChangeVisibility()}>
{isVisible ? "hide" : "show"}
</button>
- <div>
- <Field name="text" component="input" type="text" />
- </div>
+ <form onSubmit={handleSubmit}>
+ <div>
+ <Field name="text" component="input" type="text" />
+ <button type="submit">submit</button>
+ </div>
+ </form>
</div>
);
}
テキストを入力して submit ボタンをクリックすると API に値が送信され、そのレスポンスにより画面が書き換わります。