- 投稿日:2020-03-16T22:20:42+09:00
ReactのStateについてかなりザックリ解説する
ReactのStateとは
ReactにおけるStateとはユーザーの動きに合わせて変わる値のことです。stateは定義することで使用可能であり、例えば以下のように定義します。
constructor(props) { super(props); this.state = {name:'hoge'}; }定義する際の最初の2行は呪文のようなものだ、くらいの認識で問題ありません。また見ての通り、stateはオブジェクトの形での定義となります。ちなみにstateの値を更新する場合は以下のように記述します。
this.setState({name:'hogehoge'});stateの値の更新は例えばクリックイベントである、onClickなどと組み合わせて使用します。このstateについてReact初学者にとってはとっつきにくいイメージがあります。サッパリ分からんという方はprogateのReact学習コース1をやることをおすすめします。
またstateとpropsの違いについてはこちらの記事が非常に分かり易く解説してくれています。
- 投稿日:2020-03-16T18:11:46+09:00
Reactにおけるマウントについてかなりザックリ解説する
マウントとは
そもそもITにおけるマウントの意味は、コンピュータに接続した周辺機器を使うことが出来る状態にすることを表します。Reactを触っていると、度々このマウントという用語が使われます。ではReactにおけるマウントの意味について見ていきましょう。
Reactにおけるマウントの意味
Reactにおけるマウントを理解するにはまず、DOMノードについて触れる必要があります。DOMとはWEBページとプログラミング言語をつなぐ機構のようなものであり、ブラウザがWEBページを読み込むと、DOMによって文書の内容を表すオブジェクトのツリー構造が構築されます。これをDOMツリーと呼びます。
このDOMツリーはノードと呼ばれる一つ一つのオブジェクトによって構成されています。そして、マウントとはこのDOMノードをDOMツリーに追加することを意味します。逆にDOMツリーからDOMノードを削除することをアンマウントと呼びます。またマウント(追加)や、アンマウント(削除)だけでなくノードへは更新を行うことも可能です。これらDOMツリー、DOMノードの管理はトップレベルAPIというもので管理することができます。
ここまでの説明でよく分からないという場合は、とりあえずWEBページで表示する内容を変更するために、モノを追加したり削除したり、更新したりすることであるという認識でOKです。
- 投稿日:2020-03-16T02:35:49+09:00
TypeScript + Webpack + Reactによるコンポーネントのnpmパッケージを作る
概要
ReactでWebアプリを開発しているときに、「このコンポーネントたちは再利用性が高いからnpmパッケージとして独立させたい」と思ったときにどうすればいいかを整理します。
こちらの記事を参考にしています。
Reactコンポーネントをnpmで公開する(GitHub Pages付き、Babel7、webpack4)この記事と異なる事情として、
- TypeScriptを使いたい
- Babel CLIではなくWebpack CLIでコンポーネントをビルドしたい(sass-loaderとかも使いたい)
というのがありました。
案外いろいろなところで詰まったので、備忘録として書き残しておきます。筆者の環境
- MacOS Catalina 10.15.3
- zsh 5.7.1
- npm 6.9.0
- yarn 1.19.0
完成品
https://github.com/kecbigmt/ts-webpack-react-components
手順
以下の手順で説明します。
- 必要なパッケージのインストール
- 必要な設定ファイルの追加・編集
- コンポーネントを作成
- トランスパイル
- 他のReactアプリからパッケージを参照する
.npmignoreを用意してnpmに公開する部分はReactコンポーネントをnpmで公開する(GitHub Pages付き、Babel7、webpack4)と同じなので、そちらに譲ります。
1. 必要なパッケージのインストール
npm init
を済ませたNodeプロジェクトに必要なパッケージをインストールしていきます。% yarn add -D react react-dom #Reactのインストール % yarn add -D webpack webpack-cli #Webpackのインストール % yarn add -D typescript ts-loader @types/react @types/react-dom #TypeScript, ts-loader, Reactの型定義ファイルのインストール2. 必要な設定ファイルの追加・編集
- webpack.config.jsの追加
- tsconfig.jsonの追加
- package.jsonの編集
をやっていきます。
webpack.config.jsの追加
TypeScriptで書かれたReactコンポーネントをWebpackでトランスパイルできるように設定します。
./webpack.config.jsconst path = require('path'); module.exports = { mode: 'development', entry: './src/index.ts', output: { filename: 'index.js', path: path.join(__dirname, 'dist'), libraryTarget: 'commonjs2', }, module: { rules: [ { test: /\.tsx?$/, exclude: /node_modeules/, use: [ 'ts-loader', ], }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], }, }
libraryTarget: 'commonjs2'
の部分が見落とされがちですが、これがないとnpmパッケージとして外から使ったときに以下のようなエラーで怒られます。Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.tsconfig.jsonの追加
ts-loaderが.tsファイルや.tsxファイルをトランスパイルするときの設定をこちらに書きます。
./tsconfig.json{ "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "esModuleInterop": true, "module": "esnext", "target": "es6", "jsx": "react", "declaration": true } }
"declaration": true
がないと型定義ファイルが生成されないため、他のTypeScriptプロジェクトから参照するときに型定義を利用できなくなってしまいます。package.jsonの編集
編集する必要があるところだけ抜粋します。
./package.json{ "name": "my-components", "main": "dist/index.js", "scripts": { "transpile": "webpack", "prepublishOnly": "yarn transpile" }, "peerDependencies": { "react": "16.13.0", "react-dom": "16.13.0" } }nameは自由です。このパッケージを利用するときに指定する名前になるので、わかりやすい名前を付けましょう。
npmパッケージとして利用されるときの基準となるパスをmainで設定し、トランスパイルのためのコマンドをscriptsに設定します。
yarn transpile
を実行すると、Webpackがwebpack.config.jsの設定を参照してトランスパイルしてくれます。 prepublishOnlyはnpm publish
でパッケージをnpmで公開するとき前に自動で実行されます。また、このパッケージを使うときに前提とする依存関係として、peerDependenciesを追加します。
3. コンポーネントを作成
% mkdir -p src/componentssrcディレクトリと、その中にcomponentsディレクトリを作成します。この中に2つコンポーネントを記述していきます。
まずButton.tsx。便宜的に、tsx内でスタイルを定義して直接当てていますが、本来はCSS Modulesを利用して外部ファイルにスタイルを書くようにしましょう。
./src/components/Button.tsximport React, { FC } from 'react'; export type ButtonProps = { label: string; } const style = { backgroundColor: '#007bff', color: '#fff', border: '0', borderRadius: '.25rem', fontSize: '1rem', } const Button: FC<ButtonProps> = ({ label }) => ( <button style={style}>{label}</button> ); export default Button;次にBadge.tsx。
./src/components/Badge.tsximport React, { FC } from 'react'; export type BadgeProps = { label: string; } const style = { backgroundColor: '#6c757d', color: '#fff', borderRadius: '.25rem', display: 'inline-block', padding: '.25rem .4rem', fontSize: '1rem', }; const Badge: FC<BadgeProps> = ({ label }) => ( <span style={style}>{label}</span> ); export default Badge;そして、srcディレクトリ直下に、2つのコンポーネントをexportするindex.tsを配置します。
./src/index.tsexport { default as Badge } from './components/Badge'; export * from './components/Badge'; export { default as Button } from './components/Button'; export * from './components/Button';それぞれ以下のようなことをしています。
- 1行目:defult exportされている関数コンポーネントにそれぞれ名前を付けて改めてexport
- 2行目:named exportされているものすべてを同じ名前のまま改めてexport(interfaceなども外部から利用できるようにする)
これで必要なファイルは揃いました。
4. トランスパイル
いよいよJSに変換します。プロジェクトルートで以下を実行してください。
% yarn transpile
✨ Done in 3.48s
のように表示されれば成功です。distディレクトリが作成され、そのなかにtsxから変換されたjsファイルが入っているはずです。
また、.d.ts
が末尾に付く型定義ファイルも一緒に生成されていると思います。プロジェクトルート ├─dist │ ├─index.js │ ├─index.d.ts │ └─components │ ├─Badge.d.ts │ └─Button.d.tsここまででパッケージの作成は完了です。
あとは.npmignoreなどを用意してnpmに公開するだけです。そのあたりの説明はこちらの記事にお譲りします。
Reactコンポーネントをnpmで公開する(GitHub Pages付き、Babel7、webpack4)
5. 他のReactアプリからパッケージを参照する
4までで作成したパッケージを、外部のReactプロジェクトから参照してみましょう。
yarn link
を利用することで、実際にパッケージを公開しなくても試すことができます。パッケージのプロジェクトルートから、以下の流れでコマンドを実行していきます。
% yarn link % cd ../ % npx create-react-app my-project --typescript % cd my-project % yarn link "my-components"まず、
yarn link
でこのパッケージを別のプロジェクトから参照できるようにします。同じPCの中だけで有効です。次に、いったんディレクトリを出てからcreate-react-appで新しいReactプロジェクトを作成します。せっかくなのでTypeScriptで作ります。
こうして作成されたプロジェクトのルートで
yarn link "パッケージの名前"
と実行すると、先ほどのパッケージを参照することができるようになります。あとはApp.tsxを編集してパッケージを利用できるか試してみましょう。
./src/App.tsximport React from 'react'; import { Badge, Button } from 'my-components'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <Badge label="My Badge"/> <Button label="My Button"/> </header> </div> ); } export default App;編集できたらブラウザで確認してみましょう。
% yarn startあんまり見栄えは良くないですが、こんなふうに表示されればOKです。
あとはこれにcss-loader, style-loader, sass-loader, url-loaderなどお好きなloaderを加えて使うことができます。
- 投稿日:2020-03-16T01:17:23+09:00
React render内の条件分岐
Reactでのrender内での出し分けをする条件分岐の書き方を学んだのでメモ。
三項演算子
const testBooloean = true; {testBooloean ? <div>testBooloean === true;</div> : <div>testBooloean === false;</div>}&& の書き方
const testBooloean = true; {testBooloean && <div>testBooloean === true; </div>}このとき注意すること!
比較する値(今回でいうtestBooloean)には必ずBooleanの値が入るようにしなきゃいけないです。例えば...
const arr = []; {arr.length && <div>test</div>}arr = [];なのになぜか「0」が表示されてしまいます。
arrの配列の中が空の場合...という条件にしたい場合は
const arr = []; // Booleanになるようにしましょう {!!arr.length && <div>test</div>};
- 投稿日:2020-03-16T01:16:02+09:00
Reactのフックの使い方②
初心者のためのReact Hooks
前回の①はこちら
https://qiita.com/Kojiro-schatten/items/6aa2c3decfe5885231e4話す内容
ReactHooks(useState, useEffect)を用いて、API連携する際の使い方
早速作ってみる。
https://gyazo.com/a2bff49f7fbfb7b79dad22aabddb5703
上記のGIFのように、クリックするとテキストの状態が変わるAPIを用いたアプリケーションを作ります。環境構築
以下が必要なファイル群とパッケージです。右のApp.jsxが既にいくつか書き込まれているのは気にしないでください。
(UserList.jsxは作らなくても問題ないです)axiosに関しては、以下のQiitaが参考になると思います。
https://qiita.com/gcyagyu/items/4d186df2e90c53228951各ファイルの中身
App.jsimport React, { useState } from "react"; import ResourceList from "./ResourceList"; const App = () => { const [resource, setResource] = useState("posts"); return ( <div> <div> <button onClick={() => setResource("posts")}>Posts</button> <button onClick={() => setResource("todos")}>Todos</button> </div> <ResourceList resource={resource} /> </div> ); }; export default App;ResourceList.jsximport React from "react"; import useResources from "./useResources"; const ResourceList = ({ resource }) => { const resources = useResources(resource); return ( <ul> {resources.map(record => ( <li key={record.id}>{record.title}</li> ))} </ul> ); }; export default ResourceList;useResources.jsximport { useState, useEffect } from "react"; import axios from "axios"; const useResources = resource => { const [resources, setResources] = useState([]); useEffect(() => { (async resource => { const response = await axios.get( `https://jsonplaceholder.typicode.com/${resource}` ); setResources(response.data); })(resource); }, [resource]); return resources; // 即時関数の形 }; export default useResources;各ファイルの解説
App.jsimport React, { useState } from "react"; import ResourceList from "./ResourceList"; import UserList from "./UserList"; const App = () => { const [resource, setResource] = useState("posts"); return ( <div> <UserList /> <div> <button onClick={() => setResource("posts")}>Posts</button> <button onClick={() => setResource("todos")}>Todos</button> </div> <ResourceList resource={resource} /> </div> ); }; export default App;まず、App.jsです。
importでは、ResourceListとUserListをimportしています。App関数の最初では、const [resource, setResource] = useState("posts");とありますね。第一引数は、
<ResourceList resource={resource} />の部分で使われています。第二引数のsetResourceは、buttonのonClickアクションで引き起こされることから、
PostsボタンかTodosボタンを押した場合、setResource()アクションが発火し、Postsならposts、Todosならtodosという引数を呼び出して、その結果をresourceのテキストを変えるということになります。では、次に、ResourceList.jsxを見ていきましょう。
ResourceList.jsximport React from "react"; import useResources from "./useResources"; const ResourceList = ({ resource }) => { const resources = useResources(resource); return ( <ul> {resources.map(record => ( <li key={record.id}>{record.title}</li> ))} </ul> ); }; export default ResourceList;ResourceListと呼ばれる関数を作り、引数にresourceをセットします。今回、returnの中身でmapメソッドを使用していますね。mapメソッドとは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。
(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
今回、keyがrecord.idになっています。表示されるのは、record.titleですね。recordの部分は、どんな名前でも構いません。次に、useResources.jsxを見ていきます。
useResources.jsximport { useState, useEffect } from "react"; import axios from "axios"; const useResources = resource => { const [resources, setResources] = useState([]); useEffect(() => { (async resource => { const response = await axios.get( `https://jsonplaceholder.typicode.com/${resource}` ); setResources(response.data); })(resource); }, [resource]); return resources; // 即時関数の形 }; export default useResources;ResourceListと同様に、resourceを引数としたuseResources関数があります。
ここで、初めてuseEffectが出てきました。useEffectは、第一引数にコールバック関数を入れて、第二引数にその関数に依存する値の配列を入れられます。依存する値が変更される度にcallbackが実行されます。今回、async awaitを使っていますが、こちら解説すると長くなるので割愛します。
さて、第一引数が() => { (async resource => { const response = await axios.get( `https://jsonplaceholder.typicode.com/${resource}` ); setResources(response.data); })(resource); },ですが、ここではアロー関数を使っています。最後に(resource)としているため、即時間数になっています。
中身のaxiosに関してですが、第一引数にはエンドポイント、第二引数に渡したいパラメータを指定します。今回axios.getとしており、jsonplaceholderからresource部分を取得するという旨が書いてあります。setResourcesの部分は、引数をresponse.dataとしており、最終的にconst [resources, setResources] = useState([])に繋がります。なので、resourcesは、setResourcesとなって、値を返すことになります。
今回、App.jsで<div> <button onClick={() => setResource('posts')} >Posts</button> <button onClick={() => setResource('todos')} >Todos</button> </div> <ResourceList resource={resource} />としているため、
setResourceには、postsの内容とtodosの内容が入っていますね。この二つを、onClickメソッドによって、resource={resource}部分の表示を変更することができます。また、postsとtodosの参照先は、
の
/posts 100posts
/todos 200todos
を指しているため、それぞれのtitleがクリックイベントによって表示されます。まとめ
Hooksを使うことで、コードの記述量が減るから、オススメです。
useState,useEffectを取り扱いましたが、他にもフックがあり、カスタムフックも作れるため、私もどんどん作成していこうと思います。