- 投稿日:2019-10-08T23:53:59+09:00
React 初心者向け「簡単Blog」- Blog一覧・検索編
簡単Blog
Blogのリスト・検索:登録・編集・削除までを行う機能を実装します。
今日はBlogの親コンポーネントとBlogの内容を表示する子コンポーネントを作って検索までやってみます。
Blogはユーザー名、タイトル、Blog内容で構成されています。コンポーネント構成
react-bootstrap設置
react-bootstrapを少し使ってみました。
npmで設置してImportするだけで簡単に使えました。
Bootstrapの説明は省略します。興味ある方は以下のリンク参照してください。
https://react-bootstrap.netlify.com/getting-started/introduction/npm install react-bootstrap bootstrapBlogの親コンポーネント実装
stateで状態を管理
子コンポーネントにPropsを利用して状態を渡す。constructorにstate定義
keyword:検索文字列
blogData: 複数のBlogを表示するように配列にするconstructor(props) { super(props) this.state = { keyword: "", blogData: [ { user_name: "吉田", title: "おはよう", content: "朝はおはよう〜" }, ... ] }eventハンドラー追加(検索文字が入力される時にkeyword状態を変更する事でViewが更新される)
constructor(props) { ... this.onChange = this.onChange.bind(this); } onChange(e) { this.setState({ keyword: e.target.value }) }render()実装
複数のコンポーネントデータからKeyword状態でFilterしたBlogInfoデータのコンポーネントを生成。
SearchテキストのValueにthis.state.keyword
を設定しているのでonChangeのe.target.value
で検索文字列が変わるたびにStateに変更を持たせて、Renderingさせる。render() { const getComponents = (data) => { data = data.filter( (blog) => { return blog.title.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1; }); data.sort(); return data.map((blog) => { return ( <div> <BlogInfo blog={blog}></BlogInfo> </div> ) }) } return ( <div> <h1>Blog</h1> <FormControl type="text" placeholder="Search" className="mr-sm-2" value={this.state.keyword} onChange={this.onChange}/> <div>{getComponents(this.state.blogData)}</div> </div> ) }Blog.js(親コンポーネントコード)
Blog.jsimport React, { Component } from 'react' import BlogInfo from './BlogInfo' import {FormControl} from 'react-bootstrap' export default class Blog extends Component { constructor(props) { super(props) this.state = { keyword: "", blogData: [ { user_name: "吉田", title: "おはよう", content: "朝はおはよう〜" }, { user_name: "高橋", title: "こんにちは", content: "昼はこんにちは〜" }, { user_name: "木村", title: "こんばんは", content: "夜はこんばんは〜" } ] } this.onChange = this.onChange.bind(this); } onChange(e) { this.setState({ keyword: e.target.value }) } render() { const getComponents = (data) => { data.sort(); data = data.filter( (blog) => { return blog.title.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1; }); return data.map((blog) => { return ( <div> <BlogInfo blog={blog}></BlogInfo> </div> ) }) } return ( <div> <h1>Blog</h1> <FormControl type="text" placeholder="Search" className="mr-sm-2" value={this.state.keyword} onChange={this.onChange}/> <div>{getComponents(this.state.blogData)}</div> </div> ) } }BlogInfo(子コンポーネント実装)
親コンポーネントで以下の様に
blog
と言う名前のPropsにBlog状態を渡している。
<BlogInfo blog={blog}></BlogInfo>
子コンポーネントからは{this.props.blog.user_name}
でそのBlog状態を受け取ることができる。
その情報を子コンポーネントはRenderingする。
CardはBootStrapのコンポーネント。BlogInfo.jsimport React, { Component } from 'react' import {Card} from 'react-bootstrap' export default class BlogInfo extends Component{ render() { return( <Card border="info" style={{ width: '18rem' }}> <Card.Header>{this.props.blog.user_name}</Card.Header> <Card.Body> <Card.Title>{this.props.blog.title}</Card.Title> <Card.Text> 内容: {this.props.blog.content} </Card.Text> </Card.Body> </Card> ) } }App.js
App.jsimport React, { Component } from 'react' import './App.css'; import Blog from './components/Blog'; class App extends Component { render() { return ( <Blog></Blog> ); } } export default App完成
- 投稿日:2019-10-08T21:57:25+09:00
next.js でページリダイレクト
軽くハマったので書いておきます。
もしかしたら間違えてるかもしれないので、その場合は指摘してもらえると
react-hooks 前提です。
next.js@9 で確認しています。
今後変わる可能性があるのでご了承を。CSR によるリダイレクト
pushState/replaceState でリダイレクトする。
render の中でnext/router
を使う。import { useRouter } from 'next/router'; const HogePage = () => { const router = useRouter(); // pushState の場合 router.push('/'); // replaceState の場合 router.replace('/'); return <div />; };SSR によるリダイレクト
HTTP header でリダイレクトする。
getInitialProps でやる。HogePage.getInitialProps = async ({ res }) => { if (res) { res.writeHead(302, { Location: '/' }); res.end(); } }
- 投稿日:2019-10-08T17:55:46+09:00
next.jsでフォントの読み込みがおかしい時の処方箋
node.jsでnpmパッケージとかのフォントファイルを読み込んでくれない(´・ω・`;)
そんなときの処方箋メモ。ディレクトリ構成は以下がベースです。
With Firebase Hosting and Typescript example1.
static
フォルダにフォントファイルだけ手動設置
- dev環境の場合は
/src/app/static
に設置- serve & deploy環境は
/src/public/static
に設置するか、package.json
(一部抜粋)を以下のように調整
- dev環境の
static
フォルダと共通じゃないので泣く泣く重複設置。他にいい方法あったら教えてください(´・ω・`;)package.json{ "scripts": { - "build-public": "cpx \"src/public/**/*.*\" \"dist/public\" -C", + "static-to-public": "cpx \"src/app/static/**/*.*\" \"src/public/static\" -C", + "build-public": "npm run static-to-public && cpx \"src/public/**/*.*\" \"dist/public\" -C", }, }2. フォントファイルの読み込み部分をコピペ&調整(ファイルパスをフォントファイルを設置したパスに変更)して、適当な部分で
import
。以下、importサンプル。
sample.css@font-face { font-family: 'sample'; src: url("/static/fonts/sample/sample.eot") format("embedded-opentype"), font-weight: normal; font-style: normal; }sample.jsimport "./sample.css"
- 投稿日:2019-10-08T16:00:15+09:00
React-memo
React
- UIを構築するための宣言型で効率的で柔軟なJavaScriptライブラリ
- 複雑なUIをコンポーネントと呼ばれる小さく独立した部品から組み立てる
- コンポーネントはpropsと呼ばれるパラメータを受け取り、renderメソッドを通じて表示するビューの階層構造(React要素)を返す
- この構造を簡単に記述できるJSXと呼ばれる構文を使用できる
- Facebook 製のオープンソース
- モバイルアプリ、デスクトップアプリ、VR
- 仮想DOMで処理を行う
- 旧来のDOM操作による状態管理をpropsやstateで抽象化
- 関数型プログラミングの考え
- あくまでもJavaScript
- 薄いライブラリ
- Reactは単方向データフロー
- 親コンポーネントから子コンポーネントにpropsが渡される
- VueやAngularはデータバインディング
- テンプレートに埋め込まれた任意の変数が別のところに紐付けられていて、その変更がリアルタイムに反映される
- アプリが複雑になりコンポーネントの階層がどんどん深くなっていくと、予期せずに値が書き換わる可能性がある
モダンなJS
- ECMAScript
- let/const
- Class構文、コンストラクタ、インスタンスメソッド、継承
- テンプレートリテラル
- スプレッド構文
- async/await
- 分割代入
- アロー関数
function plusOne(n) { return n + 1; } const plusOne = (n) => { return n + 1; }; const plusOne = n => n + 1; const plusOne = (n: number): number => n + 1;関数型プログラミング
- コレクションの反復処理
- map()、filter()、find()、every()、some()、includes()、reduce()、sort()
- 無名関数
- 変数に無名関数を代入することで命名できる
- 高階関数
- 部分適用
- 複数の高階関数を合成できる
- カリー化
TypeScript
- 静的型付け
- 型推論
- Null安全性
Babel
- トランスコンパイル(現行のブラウザ環境で正常に動作する形式に変換)
コンポーネント
- 再利用可能なパーツ
- 自分で自分の状態を管理
- 基本的にComponentを作って組み合わせて アプリケーションを作っていく
- JSXを使ったりして記述
- Local state
- 関数コンポーネント
- Presentational Components
- Container Components
- Recompose
- Stateless Functional Components
- propsは不変のobject、引数に相当
- stateは変更可能なobject、コンポーネント自身のみ
- Stateの保持はReact.Componentを継承するクラスでしか使用できない
import React, { Component } from 'react'; // Class構文を使ってreactパッケージ内のComponentを継承している class Hello extends Component { render() { return <div>Hello, world!</div>; } } export default Hello;import Reat from 'react'; // 通常の関数構文のみで定義している const Hello = () => { return <p>Hello, world!</p>; } export default Hello;ライフサイクルメソッド
- コンポーネントがMount、Update、Unmoutされるタイミングで呼ばれるメソッド
Atomic Design
Redux
- React が扱うstate を一元管理するためのフレームワーク
- アプリが大きくなってComponent同士が依存しあうようになると stateの管理をすることが大変になる
- Reduxを使うことで、ReactのComponentではstateを管理せずにpropsだけを使うようにできる
- SPAなど複雑化するReactステート管理を、 ルール(哲学)に従って書かせることで、フロントエンドの動きを人間にとって追いやすくする
- 信頼できる単一の情報源、読み取り専用の状態(Actions)、純粋な関数(Reducers)によって変更される
HOC
- HOC(Higher-order-components)コンポーネントを合成する仕組み
- コンポーネントに関数を適応し、機能が合成されたコンポーネントを返す
- propsを新しく加えたり、ライフサイクルメソッドを追加することも可能
- コンポーネントに新たな機能を追加して使い回しやすくできる
- material-ui などすでに作成されたコンポーネントにも柔軟に適応可能
- HOC自体を他コンポーネントにも使いまわせる(ログイン必須コンポーネントなど)
- 継承より合成やUIに徹した哲学
- コンポーネント関数を適応し、機能が合成されたコンポーネントを返す
参考
- 投稿日:2019-10-08T10:23:05+09:00
Stateのネストした要素にアクセスする (React)
Reactで値を保持する箇所といえばstateですが
stateでネストしたプロパティを変更することがよくあります。
何回も調べるのがおっくうになってきましたので
解決方法の1つを記事にします
手っ取り早い動作を見たい場合は ↓に実行動作環境を用意しました
https://codesandbox.io/embed/react-typescript-playground-c89q5
どういうコードなの?
人間(humanDetail) の詳細に 名前、年齢、性別を設けたんすけど
今回は tom さんの年齢を 18 ⇒20に変更するというコードです。
//PlayGroundからコピペしたから、importは割と適当だよ //importは適宜変更してね import * as React from "react"; import { render } from "react-dom"; import { Button } from "antd"; import "antd/dist/antd.css"; interface humenDetail { name: string; age: number; sex: boolean; // true:men false:women } interface Props {} interface State { human: humenDetail | null; } class App extends React.Component<Props, State> { constructor(props: Props) { super(props); this.state = { human: { name: "tom", age: 18, sex: true } }; } nestedStateChange = () => { const item = this.state.human; if (item !== null) { item.age = 20; //ここに変更する値 } this.setState({ human: item }); console.log(this.state.human); }; render() { return ( <div> <Button onClick={this.nestedStateChange}> ここを押すと、値が18⇒20に変わるよ </Button> <div>{this.state.human.age}</div> </div> ); } } render(<App />, document.getElementById("root"));nestedStateChangeメソッドで ネストする前のプロパティを itemとして指定して、itemのプロパティに値を入れて
item を setStateするよーっていうオーソドックスなやり方です。
- 投稿日:2019-10-08T10:23:05+09:00
ネストした Stateの要素を setStateで変更する (React)
解決する課題
Reactで値を保持する箇所といえばstate
setStateメソッドで、stateの値を変更できますが、
ネストされた要素は直接 setStateで変更できません (React 16.2現在)しかしながらstateでネストした要素を変更することがよくあります。
思ったより以上に情報が少ない為、
単純な解決方法を1つを記事にします
結論 (コード用意しました)
https://codesandbox.io/embed/react-typescript-playground-c89q5
どういうコードなの?
人間(humanDetail) の詳細に 名前、年齢、性別のプロパティを設けます
今回は tom さんの年齢を 18 ⇒20に変更するというコードです。
//PlayGroundからコピペしたから、importは割と適当だよ //importは適宜変更してね import * as React from "react"; import { render } from "react-dom"; import { Button } from "antd"; import "antd/dist/antd.css"; interface humenDetail { name: string; age: number; sex: boolean; // true:men false:women } interface Props {} interface State { human: humenDetail | null; } class App extends React.Component<Props, State> { constructor(props: Props) { super(props); this.state = { human: { name: "tom", age: 18, sex: true } }; } nestedStateChange = () => { const item = this.state.human; if (item !== null) { item.age = 20; //ここに変更する値 } this.setState({ human: item }); console.log(this.state.human); }; render() { return ( <div> <Button onClick={this.nestedStateChange}> ここを押すと、値が18⇒20に変わるよ </Button> <div>{this.state.human.age}</div> </div> ); } } render(<App />, document.getElementById("root"));nestedStateChangeメソッドで ネストする前のプロパティを itemとして指定して、itemのプロパティに値を入れて
item を setStateするよーっていうオーソドックスなやり方です。
- 投稿日:2019-10-08T10:23:05+09:00
ネスト(nest)した State の要素を setState で変更する(React)
解決する課題
Reactで値を保持する箇所といえばstate
setStateメソッドで、stateの値を変更できますが、
ネストされた要素は直接 setStateで変更できません (React 16.2現在)しかしながらstateでネストした要素を変更することがよくあります。
思ったより以上に情報が少ない為、
単純な解決方法を1つを記事にします
Typescriptを利用します。
Javascriptよりもコードはわかりやすく見えると思います。
結論 (コード用意しました)
https://codesandbox.io/embed/react-typescript-playground-c89q5
どういうコードなの?
人間(humanDetail) の詳細に 名前、年齢、性別のプロパティを設けます
今回は tom さんの年齢を 18 ⇒20に変更するというコードです。
//PlayGroundからコピペしたから、importは割と適当だよ //importは適宜変更してね import * as React from "react"; import { render } from "react-dom"; import { Button } from "antd"; import "antd/dist/antd.css"; interface humenDetail { name: string; age: number; sex: boolean; // true:men false:women } interface Props {} interface State { human: humenDetail | null; } class App extends React.Component<Props, State> { constructor(props: Props) { super(props); this.state = { human: { name: "tom", age: 18, sex: true } }; } nestedStateChange = () => { const item = this.state.human; if (item !== null) { item.age = 20; //ここに変更する値 } this.setState({ human: item }); console.log(this.state.human); }; render() { return ( <div> <Button onClick={this.nestedStateChange}> ここを押すと、値が18⇒20に変わるよ </Button> <div>{this.state.human.age}</div> </div> ); } } render(<App />, document.getElementById("root"));nestedStateChangeメソッドで ネストする前のプロパティを itemとして指定して、itemのプロパティに値を入れて
item を setStateするよーっていうオーソドックスなやり方です。
- 投稿日:2019-10-08T10:23:05+09:00
ネストした State の要素を setState で変更する(React)
解決する課題
Reactで値を保持する箇所といえばstate
setStateメソッドで、stateの値を変更できますが、
ネストされた要素は直接 setStateで変更できません (React 16.2現在)しかしながらstateでネストした要素を変更することがよくあります。
思ったより以上に情報が少ない為、
単純な解決方法を1つを記事にします
Typescriptを利用します。
Javascriptよりもコードはわかりやすく見えると思います。
結論 (コード用意しました)
https://codesandbox.io/embed/react-typescript-playground-c89q5
どういうコードなの?
人間(humanDetail) の詳細に 名前、年齢、性別のプロパティを設けます
今回は tom さんの年齢を 18 ⇒20に変更するというコードです。
//PlayGroundからコピペしたから、importは割と適当だよ //importは適宜変更してね import * as React from "react"; import { render } from "react-dom"; import { Button } from "antd"; import "antd/dist/antd.css"; interface humenDetail { name: string; age: number; sex: boolean; // true:men false:women } interface Props {} interface State { human: humenDetail | null; } class App extends React.Component<Props, State> { constructor(props: Props) { super(props); this.state = { human: { name: "tom", age: 18, sex: true } }; } nestedStateChange = () => { const item = this.state.human; if (item !== null) { item.age = 20; //ここに変更する値 } this.setState({ human: item }); console.log(this.state.human); }; render() { return ( <div> <Button onClick={this.nestedStateChange}> ここを押すと、値が18⇒20に変わるよ </Button> <div>{this.state.human.age}</div> </div> ); } } render(<App />, document.getElementById("root"));nestedStateChangeメソッドで ネストする前のプロパティを itemとして指定して、itemのプロパティに値を入れて
item を setStateするよーっていうオーソドックスなやり方です。
- 投稿日:2019-10-08T10:10:27+09:00
5分でわかる「今更だけどReactってなに?」
2年ぶりくらいに業務で React をやることになったので、個人的なおさらい用です。
React ってなに?
React は UI を作るためのライブラリです。
UI は「コンポーネント」という概念によって記述することになります。
基本的にはユーザーが自由に実装した「カスタムコンポーネント」を UI 毎に作成し、それを組み立てることで Web アプリケーションを構築することができます。
HTML の DOM 要素を利用したコンポーネントを作成するにはReact.DOM
オブジェクトを利用しすることもできます。React の特徴
以下の4つが React の特徴です。
- リアクティブ
- 仮想 DOM
- Component ベース
- JSX
リアクティブであること
ユーザーの操作やデータの変化を監視して、その変化によって自分で設定した処理に沿うように自動的に画面を再描画してくれます。
仮想 DOM
jQuery や一般的な JavaScript では DOM 操作を「最適化して」処理を記述する必要があります。
DOM 操作は非常にコストの大きい処理です。近年の Web 開発では非常に多くの DOM 操作を必要とします。ユーザーの操作に応じて多くのデータを更新したり、見た目を変化させるためです。React では、仮想 DOM という React が内部に持っている独自の DOM 情報と実際の DOM を効率的な方法で比較し、差分を変更してくれるので、処理が非常に軽くなります。
注意点としては、DOM 操作を必要としないわけではなく、純粋な JavaScript を最適化した処理よりも早くなることはありません。
ですが規模の大きいアプリケーションでは全てを最適化することは現実的ではありません。
あくまで、楽にいい感じにしてくれるもの、と捉えておくと良さそうです。Component ベース
React ってなに? でも書いたように、React はコンポーネントを作って、それを組み立てて Web 開発を行います。
では、それによるメリットはなんでしょうか。
それは、「保守性」「再利用性」「デザインシステムとの相性の良さ」が挙げられます。保守性
コンポーネント単位で開発していくと、しっかりと設計を行うことでコンポーネントに「単一責任の原則」が生まれます。
機能・見た目・状態などをそのコンポーネントに閉じ込め、外から変更をできなくすることができます。
それにより機能の所在が明らかに別れてデバッグがしやすくなります。再利用性
保守性で書いた単一責任の原則は再利用性の向上にも寄与します。
もしそのコンポーネントが複雑な依存関係を持っていない場合、複数の場所でコンポーネントを再利用することが可能となり、開発スピードが上がります。デザインシステムとの相性の良さ
近年ではアプリケーション開発にデザインシステムを持ち込むことが多くなっています。
例えば Atomic Design のように Web アプリ全体をコンポーネントで分け、それを役割によって分類・管理し、それを組み合わせていくようなことが、コンポーネントベースであれば簡単に行うことができます。JSX
JSX も React の特徴の一つです。
JSX は JavaScript の拡張構文で、JavaScript の中に文書構造を持ち込むことができるという特徴があります。
簡単にいうと、JavaScript の中で HTML が書けて、属性や文字列に変数やメソッドを埋め込むことができるというものです。なぜ JSX が必要なのか、不思議に思う方もいると思います。
HTML と JavaScript は分離して管理すべきだ!という思想が広く定着していたこともあります。
ですが近年の Web 開発では、データをサーバーから API で動的に表示させることが非常に多いです。ほぼ実態のない HTML 要素に JavaScript の処理を紐付けることで、意味のない空の
div
や空のa
タグが散乱し、文書構造もなにもあったものではない状況です。
そのために WAI-ARIA という仕様が取り沙汰されましたが、先程も述べたように近年の Web 開発では「動的に」表示が変化します。つまり内容が変わってしまうのです。それならば、JSX で記述する WAI-ARIA の状態を管理してあげたほうが、より現実的なセマンティクス Web の実現につながるのではないかという意見もあり、採用しているプロダクトが多いようです。
Accessible Rich Internet Applications (WAI-ARIA) 1.2 日本語訳
なんで React や Vue のような JavaScript ライブラリが人気なのか
ここまで React の特徴を書いた中ですでに React に対して「高機能な近年の Web アプリ開発に適していること」を感じた人も多いのではないでしょうか。
- 肥大化した処理の DOM 操作の最適化
- コンポーネントによる管理のしやすさ
- JSX によるアプリケーション化した Web の管理の最適化
これらは Vue などの、どのライブラリにも大抵は共通しており、細かな振る舞いやスケーリングの思想の違いによって使い分けられています。
コンポーネントの最小構成
最小限の構成は
render()
という、カスタムコンポーネント(ユーザーが定義したコンポーネント)に必須のメソッドで React のコンポーネントを返す形です。コンポーネントに「プロパティ(props)」を追加すると、その値に応じてふるまいを変更できます。
追加したすべてのプロパティはthis.props
でアクセスすることができ、例えば、親コンポーネントから、子孫コンポーネントに情報を渡す際に非常に便利です。また、コンポーネントには
propTypes
というプロパティも追加でき、そのコンポーネントが受け付けるプロパティの名前や値の型を宣言することができます。
必須ではありませんが、データを検証することができるし、コンポーネントの仕様としても機能するため、メンテナビリティが向上するため推奨されています。
更に、getDefaultProps()
というメソッドを作成して設定することで、省略可能なプロパティのデフォルト値をオブジェクトで返すこともできます。プロパティは変更ができないデータ(immtable data)として扱われ、
getDefaultProps()
で定義したデフォルト値か、親コンポーネントから受け取った値のどちらかを持っています。状態の管理(ステート)
コンポーネントは描画する際にステートのデータを利用します。
ステートに変化があると、React は自動的に UI を再構成してくれます。つまり、最初にrender()
で初期描画をしたら、ユーザーの操作やデータの更新を見てrender()
メソッドの中で設定した表示方法に沿って UI を自動的に最適化してくれるというわけです。ステートにアクセスするには
this.state
オブジェクトを使うことでコンポーネントからアクセスできます。
ステートを変更するにはthis.setState()
を使い、このメソッドが呼ばれるとrender()
メソッドを呼び出して UI を更新してくれます。ステートはコンポーネント固有のデータで、データはコンポーネントからコンポーネントへ受け渡すことはしません。
コンポーネントが持つプライベート変数のようなものだと認識しておけばよさそうですね。まとめ
ここまでお読みいただきありがとうございます!
この記事を読んで実際に触ってみたいと思った方はぜひ公式のチュートリアルを試してみてください!
導入からちょっとしたゲームの作成ができて、時間もそれほど必要ありません。(そして日本語です!)誤りや認識の違いなどがありましたらご指摘いただけると助かります!
- 投稿日:2019-10-08T09:10:09+09:00
Reactの入力フォームを仮想ウインドウ化する
Reactの入力フォームを仮想ウインドウ化する
ユーザ入力用のUIの位置決めが面倒くさい
フロントエンドのUIを作る際、ユーザ入力を行うための場所をわざわざ作るのはそれなりに手間のかかる作業です。特に配置場所を作るのがそれなりに面倒です。今回はこれをできる限り簡単に解決する方法を解説していきたいと思います。
こちらの記事でToDoを入力するためのアプリケーションを作ったので、その入力フォームを改造していきます。
React-Reduxが難しい? それは過去の話だ! ~ ToDoアプリを最小限の労力で記述する ~
今回必要とするコンポーネント
https://www.npmjs.com/package/@jswf/reactインストールの方法
npm -D i @jswf/react仮想ウインドウを利用する上でやるべき事
フォームの該当箇所をJSWindowで囲む
<JSWindow>~</JSWindow>以上で入力フォームの仮想ウインドウ化は完了しました。お疲れ様です。
前回のプログラムを変更するとこんな感じです。
function FormComponent() { const todoModule = useModule(TodoModule); return ( <JSWindow>{/*←追加*/} <div style={{ textAlign: "center" }}> <div> <div>タイトル</div> <input style={{ width: "20em" }} value={todoModule.getState("input", "title")!} onChange={e => todoModule.setState(e.target.value, "input", "title")} /> <div>説明</div> <textarea style={{ width: "20em", height: "5em" }} value={todoModule.getState("input", "desc")!} onChange={e => todoModule.setState(e.target.value, "input", "desc")} /> <div> <button onClick={() => todoModule.addTodo()}>Todoを作成</button> </div> </div> </div> <JSWindow>{/*←追加*/} ); }足したのはJSWindowだけです。これによって、以下のように仮想ウインドウ上にコンテンツが表示されるようになりました。この仮想ウインドウはサイズが可変、移動、最大化、最小化の機能がデフォルトで付いています。
この仮想ウインドウ、閉じるボタンが搭載されているので押したら消えます。そして二度とToDOを書き込むことは出来なくなるという自由が手に出来てしまいます。
流石にそれでは困るので、閉じたウインドウを再び開けるようにします。
interface TodoState { //入力中データの保持 input: { title: string; desc: string; }; //TODOリスト todos: { id: number; title: string; desc: string; done: boolean; }[]; //TODOのID附番表index index: number; //ウインドウの状態 windowState: WindowState; //<----- 追加 } //初期値 protected static defaultState: TodoState = { todos: [], input: { title: "", desc: "" }, index: 0, windowState: WindowState.HIDE //<----- 初期状態の追加 }; function FormComponent() { const todoModule = useModule(TodoModule); return ( <> {/* ボタンの追加 */} <button onClick={()=>todoModule.setState({ WindowState: WindowState.NORMAL })} > ウインドウの表示 </button> {/* タイトルとウインドウ状態設定を追加 */} <JSWindow title="ToDoの入力" windowState={todoModule.getState("WindowState")!}> <div style={{ textAlign: "center" }}> <div> <div>タイトル</div> <input style={{ width: "20em" }} value={todoModule.getState("input", "title")!} onChange={e => todoModule.setState(e.target.value, "input", "title") } /> <div>説明</div> <textarea style={{ width: "20em", height: "5em" }} value={todoModule.getState("input", "desc")!} onChange={e => todoModule.setState(e.target.value, "input", "desc") } /> <div> <button onClick={() => todoModule.addTodo()}>Todoを作成</button> </div> </div> </div> </JSWindow> </> ); }windowStateを設定すると、仮想ウインドウの状態を変更することが出来ます。ということで、ボタンを押したらステータスをNORMALに戻し再表示出来るようになりました。
まとめ
この仮想ウインドウコンポーネントは複数表示やネストも可能です。詳細な機能に関しては
Reactで超簡単、タグで挟み込むだけの仮想ウインドウの実装
の記事で説明しています。