Edited at

Electron & React & Redux & TypeScript アプリ作成ワークショップ 6日目


概要

前回までにリストの表示ができました。

今回は、タスクの新規登録時にテキストボックスに値が入力できるようにします。

以前のチュートリアルでは、テキストボックスの変更を Action をReducer に送って更新していましたが、ここではローカルステートを利用してみます。


ローカルステート

ここでは、ローカルステートを利用します。ローカルステートとは、コンポーネント内のスコープに保持するデータです。

Reactでは、テキストボックスに入力した値は、コンポーネントのプロパティか、ローカルステートとバインド(UIとデータのひも付け)する必要があります。しかし、Reactのバインドは一方向(データ → UI)ですので、UI の入力をプロパティか、ローカルステートに反映しなければ更新されません。

プロパティを更新するには、Action を生成して Reducer に送る必要がありますが、ローカルステートであれば、コンポーネント内でデータを変更することができるので、手軽でレスポンスも良いです。


AddTask コンポーネントに、ステートを追加する

AddTask.tsx に、インターフェースでステートを追加します。


AddTask.tsx

interface IProps {

/** タスク名 */
taskName: string;
/** 期限 */
deadline: Date;
}

interface ILocalState {
/** タスク名 */
taskName: string;
/** 期限 */
deadline: Date;
}


これを、コンポーネントクラスの定義で、2番めのジェネリック型に指定します。


AddTask.tsx

export class AddTask extends React.Component<IProps, ILocalState> {

// ~~~~~~~~~~~
// (略)
}


ローカルステートへのアクセス

ローカルステートのデータを参照するには、this.state で参照できます。

ただし、ステートの値を更新するには必ず this.setState メソッドを利用します。そうでないと、state の変更を render に伝えられなくなります。

また、setState では、すべての項目を指定する必要はありません。React が自動的に現在のステートにマージしてくれます。

例:


AddTask.tsx

console.log(this.state); // {taskName: '変更前の値', deadline: '2018-9-18 08:57'}

this.setState({
taskName: '変更後の値'
});
console.log(this.state); // {taskName: '変更後の値', deadline: '2018-9-18 08:57'}



ローカルステートを初期化する

ローカルステートは、コンポーネントを作成したときに初期化する必要があります。

クラスのコンストラクタで記述します。

コンストラクタでの初期化では、直接 this.state に値を代入することができます。


AddTask.tsx

export class AddTask extends React.Component<IProps, ILocalState> {

// ↓ 追加
public constructor(props: IProps) {
super(props);
this.state = {
deadline: props.deadline,
taskName: props.taskName,
};
}
// ↑ 追加
// (略)
}


コントロールにバインドする

前は、タスク名や期日のコントロールに this.propstaskNamedeadline をバインドしていたので、これを、 this.state のものに書き換えます。


AddTask.tsx

export class AddTask extends React.Component<IProps, ILocalState> {

// (略)
public render() {
const date = Moment(this.state.deadline); // ← 変更
const taskNameId = UUID();
const deadlineId = UUID();
return (
<Container>
<TaskNameBox>
<label htmlFor={taskNameId}>task name</label>
<TextBox id={taskNameId} type="text" value={this.state.taskName /* <- 変更 */}
onChange={() => {/* ここは後で */ }} />
</TaskNameBox>
<DeadlineBox>
<label htmlFor={deadlineId}>dead line</label>
<DatePicker selected={date} showTimeSelect={true}
dateFormat="YYYY-MM-DD HH:mm" onChange={() => {/* ここは後で */ }} />
</DeadlineBox>
<AddButton onClick={this.onClickAdd}>+</AddButton>
</Container>
);
}
// (略)
}


コントロールのイベントを割り当てる

タスク名や期日を変更したときに、ローカルステートを更新するイベントハンドラを作成します。

また、タスク追加ボタンを押した時に、this.prop の値を渡していましたが、変更した最新の情報は、this.state にあるので、こちらのものを渡すように修正します。

AddTask クラス内のプライベート・メソッドとして定義します。


AddTask.tsx

export class AddTask extends React.Component<IProps, ILocalState> {

// (略)
/**
* 追加ボタンを押すと、タスク一覧にタスクを追加する
*/

private onClickAdd = (e: React.MouseEvent) => {
store.dispatch(createAddTaskAction(this.state.taskName, this.state.deadline)); // ← 変更
const m = Moment(new Date()).add(1, 'days');
this.setState({
deadline: m.toDate(),
taskName: '',
});
}
// ↓ 追加
/**
* タスク名変更イベントハンドラ
*
* テキストボックスの内容をローカルステートに反映する
*/

private onChangeTaskName = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
taskName: e.target.value,
});
}
/**
* 期日を変更したときのイベントハンドラ
*
* 変更した日付をローカルステートに反映する
* DatePickerの独自プロパティで、引数として日付が渡される
*/

private onChangeDeadLine = (date: Moment.Moment| null) => {
this.setState({
deadline: !!date ? date.toDate() : new Date(),
});
}
// ↑ 追加
}

追加したメソッドをそれぞれのコントロールの onChange に割り当てます。


AddTask.tsx

    public render() {

const date = Moment(this.state.deadline);
const taskNameId = UUID();
const deadlineId = UUID();
return (
<Container>
<TaskNameBox>
<label htmlFor={taskNameId}>task name</label>
<TextBox id={taskNameId} type="text" value={this.state.taskName}
onChange={this.onChangeTaskName /*← 変更*/} />
</TaskNameBox>
<DeadlineBox>
<label htmlFor={deadlineId}>dead line</label>
<DatePicker selected={date} showTimeSelect={true}
dateFormat="YYYY-MM-DD HH:mm" onChange={this.onChangeDeadLine /*← 変更*/} />
</DeadlineBox>
<AddButton onClick={this.onClickAdd}>+</AddButton>
</Container>
);
}


ビルドして確認する

修正は以上で完了です。ビルドして動作を確認してください。

$ npm run build && npm start

新しいタスクのテキストと日付が変更でき、追加ボタンでタスクの追加ができることを確認してください。


次回

殆どの機能が実現できましたが、タスクの情報がダミーのデータを表示しており、永続化もされません。

次回は、ファイルへの非同期のI/Oと、それに伴う Redux での非同期処理について説明します。