- 投稿日:2020-05-24T22:26:33+09:00
Reactの基礎
- 投稿日:2020-05-24T21:07:55+09:00
ディレクトリ下の画像ファイルを動的に読み込む
やりたいこと
特定のディレクトリ下に画像ファイルを追加したらコード自体には一切手を加えずその画像が表示されるようにしたい。
結論
import React from 'react'; function App() { const importAll = (r: __WebpackModuleApi.RequireContext) => { return r.keys().map(v => r(v) as string); } const images = importAll(require.context('./assets/images', false, /\.(png|jpe?g|svg)$/)); return ( <> {images.map(image => <img src={image} key={image} alt={image} />)} </> ); }
require.context()
で読み込むファイルを探すディレクトリと正規表現を指定して対象ファイルのパスを取得できる。RequireContextの戻り値がanyなので、 公式ドキュメントのように
return r.keys().map(r)
するとunknown[]
となる。
今回は画像を読み込んでいるためsrcに突っ込むためにstringにキャストしている。ドキュメント
https://webpack.js.org/guides/dependency-management/#context-module-api
参考記事
https://qiita.com/proudust/items/d716957e243f9e019fda
https://qiita.com/jkr_2255/items/d23e66323857d3189a00
- 投稿日:2020-05-24T18:54:53+09:00
ReactでSkyWayを試す
はじめに
この記事はとあるハッカソンのために事前勉強したものが特に使われることがなかったのでその供養のために書きます。
今回はskyway-jsをReactで試してみたという内容です。実装した内容は本当にチュートリアル程度のものなのですが、意外とReact(nativeじゃない方)の記事がなかったので書いてみようと思いました。つくったもの
こちらで公開しています。単純にお互いのIDを交換しあって1対1のビデオチャットができるというものです。SkyWayを用いることで簡単にビデオチャットをアプリに実装することができます。
開発環境
デプロイ環境としてVercelを使ってみました。理由は、もともとこのSkyWayをNext.jsでやろうと思っていてその際に公式から推奨されるものであったのがこれだったからです。しかしNext.jsはskyway-jsのインポートがうまく行かなかったので途中で断念しました。
なので今回もcreate-react-app
を使ってReactの環境を構築しました。実装内容
実装したのはこれだけです。
import React from 'react' import { useState, useRef } from 'react' import './App.css' import Peer from 'skyway-js' const peer = new Peer({ key: process.env.REACT_APP_SKYWAY_KEY }) const App = () => { const [myId, setMyId] = useState('') const [callId, setCallId] = useState('') const localVideo = useRef(null) const remoteVideo = useRef(null) peer.on('open', () => { setMyId(peer.id) navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(localStream => { localVideo.current.srcObject = localStream }) }) peer.on('call', mediaConnection => { mediaConnection.answer(localVideo.current.srcObject) mediaConnection.on('stream', async stream => { remoteVideo.current.srcObject = stream }) }) const makeCall = () => { const mediaConnection = peer.call(callId, localVideo.current.srcObject) mediaConnection.on('stream', async stream => { remoteVideo.current.srcObject = stream await remoteVideo.current.play().catch(console.error) }) } return ( <div> <div>skyway test</div> <div><video width="400px" autoPlay muted playsInline ref={localVideo}></video></div> <div>{myId}</div> <div> <input value={callId} onChange={e => setCallId(e.target.value)}></input> <button onClick={makeCall}>発信</button> </div> <div><video width="400px" autoPlay muted playsInline ref={remoteVideo}></video></div> </div> ) } export default App
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
を使って、内蔵カメラ・マイクから取り込んだストリームを<video>
のsrcObject
に取り込むReactでの良い方法がわからなかったのでuseRef
を使って無理くりやりました。
コード全体はこちらで共有してますのでもう少し良い方法をご存知でしたらPRいただきたいです。さいごに
いやー、SkyWayめちゃ簡単ですね!少し前に書いたこの記事のやつとかと組み合わせて何かやれたら面白そうだなと思いました。
とりあえず今回のハッカソンで使うことはなかったのですが、そのうちどこかで使おうかなと思います。
- 投稿日:2020-05-24T17:37:27+09:00
備忘:Monaca (Onsen UI V2) + React + ncmb
前提
- MonacaでReactフレームワークを使ったプロジェクトがある状態
- テンプレートはOnsen UI V2 Tabbar
- ニフクラMobile Backendでアプリがある状態
- アプリのapiKeyとclientKeyが必要
ncmbのインストール
ターミナルからインストールすると
/node_modules/ncmb
が追加される.npm install ncmb -S
参考:https://www.npmjs.com/package/ncmb
ncmb APIを使うコードを追加
手っ取り早く試すために,
/src/HomePage.jsx
に以下を追加.
(まだReact勉強中だけど,本当はvar
じゃなくてconst
とかlet
を使うべき?)export default class HomePage extends React.Component { render() { + var apiKey = "your_ncmb_apiKey"; + var clientKey = "your_ncmb_clientKey"; + var NCMB = require('ncmb'); + var ncmb = new NCMB(apiKey, clientKey); + if (!ncmb) { + console.log("ncmb is null."); + } else { + console.log("ncmb is loaded."); + } return ( <Page renderToolbar={() =>
fsの解決
このままだと
ERROR in ./node_modules/node-localstorage/LocalStorage.js Module not found: Error: Can't resolve 'fs' in '...\node_modules\node-localstorage'
というエラーがでるので,
/webpack.config.js
に以下を追加.performance: { hints: false - } + }, + + node: { + fs: 'empty' } };参考:
https://github.com/lmaccherone/node-localstorage/issues/45
https://github.com/lmaccherone/node-localstorage/issues/35
https://qiita.com/Hoshito/items/f7acb1b2082f8c2d0bd3apikey and clientkey requredの解決
コンパイル(トランスパイル?)は解決するけど,実行結果を見るとエラーが出る.
vendors~app.js:40 Error: apikey and clientkey required at Object.e (vendors~app.js:48) at Object.<anonymous> (vendors~app.js:48) at l (runtime~app.js:1) at t.value (app.js:1) at Ba (vendors~app.js:40) at Da (vendors~app.js:40) at vs (vendors~app.js:40) at lu (vendors~app.js:40) at su (vendors~app.js:40) at Qs (vendors~app.js:40)これは新しいWebpackのDefineの使い方に問題があるっぽい?
とりあえず,/node_modules/ncmb/lib/ncmb.js
を編集して,問題箇所をコメントアウト.return NCMB; })(); + /* if (typeof define === 'function' && define.amd) { define([], NCMB); } + */ if(typeof window !== "undefined"){ window.NCMB = NCMB; }参考:
https://github.com/NIFCloud-mbaas/UserCommunity/issues/456
https://github.com/for-GET/know-your-http-well/issues/60
https://github.com/webpack/webpack/issues/5316#issuecomment-395778081
https://www.npmjs.com/package/amdefine#amdefineintercept-usage結果
とりあえず,以上のステップで,ncmb APIが使えるようになった.
(確認したのは,login()
だけ)ncmb is loaded.
- 投稿日:2020-05-24T12:17:28+09:00
Reduxによる状態管理の仕組みを理解しよう
目次
概要
この記事では、状態管理を行うためのフレームワークであるReduxの基礎や状態管理の仕組みについてまとめています。現在Reduxについて勉強中の方の参考になるようでしたら幸いです。
なお本記事はReactを用いていることを前提条件としています。(注意)
またReact ComponentとRedux Storeを関連づける手法として、現在はHooksとReduxを用いた手法もありますが、今回は従来のconnect関数を用いた手法で紹介しています。後日Hooksを用いた手法についても投稿予定です。Reduxとは
Reduxとは、上記でも述べたようにReactの状態(state)を管理するフレームワークです。
またReduxはReactと併用することを想定して生み出されているため、Reactと非常に相性が良いとされています。ReduxはFluxアーキテクチャの一つで、コンポーネントの数が多くなったときに簡単にstateを共有するための手段として利用されています。
またReduxなどのFluxアーキテクチャの最大の特長はデータフローが単方向で構築できることで、規模が大きくなった場合にもデータの流れを見失いにくくなります。(後ほど図解)
Reduxを使用するためには以下のコマンドでインストールしておく必要があります。
$ yarn add redux react-redux
Reduxの要素
Reduxによる状態管理を行うための構成要素には主に以下の4つが必要となります。
- Action:アプリケーション内でなにが起きたのかを示すオブジェクト(データ)
- Reducer:Actionのtype(種類)に応じてstateを変化させるメソッド
- Store:アプリケーション内の全てのstateを保持している場所
- State:アプリケーションの状態
Reduxのデータフロー
上記のような構成にすることで、先ほど述べたデータフローの単方向化を実現することが可能にすることができます。
- ActionCreatorによってActionを生成
- Actionをdispatch
- ReducerでStore内のStateを更新
- Store内のStateをReact Componentで参照(ReactとReduxの連携)
では次にAction、Reducer、Store、ReactとReduxの連携方法について詳しく説明します。
(stateについては省略します)Action
Actionの特徴は以下のことが挙げられます。
- アプリケーションの中でなにが起きたかを示すオブジェクト(データ)である
type
とそれに対応する値を持つ(typeの値はユニークなものにする)- Storeの唯一の情報源
- ActionCreatorによって生成される
ActionCreator
Actionを作成するメソッド
FluxにもActionCreatorがありますが、ReduxではActionを作成するのみでStoreへのdispatchは行わないという違いがあります。例えばToDoリストアプリケーションでリストを追加したいときにはActionCreatorと組み合わせて次のように記述します。
const ADD_TODO = 'ADD_TODO'; // Action Creator const addTodo = text => { return { // Action type: ADD_TODO, text } }前述の通り、ActionCreatorによって生成されたActionは、生成されたのみでStoreへdispatchされていません。
Reduxではdispatch()
メソッドによってActionをStoreに送ることができます。dispatch(addTodo(text));Reducer
Reducerの特徴は以下のことが挙げられます。
- Actionの
type
に応じて状態をどう変化させるのかを定義したメソッドである- 引数のstateを更新するのではなく、新しいStateを作成して返す
- 純粋関数でないとならない(毎回必ず同じ結果を返す)
Reducerの実装手順は以下のようになります。
- 状態(state)はオブジェクトとして初期値を定義
- Reducerは関数として定義、引数は2つ(state, action)
- Actionのtypeに応じて状態を変化させ結果を返す
コードで見ると以下のようなイメージになります。
src/reducers/reducer1import { ADD_TODO, REMOVE_TODO } from '../actions'; const initialState = { text: 'initial text' }; export default (state = initialState, action) => { switch(action.type) { case ADD_TODO: return { // ActionがADD_TODOの場合のState更新処理 }; case REMOVE_ADD: return { // ActionがREMOVE_TODOの場合のState更新処理 }; default: return state; // Actionのtypeに当てはまらない場合はデフォルトを返す } };ToDoリストとして正しいinitialStateの設定かは分かりませんが、Reducerの実装イメージとしてはこんな感じになります。
またReduxではアプリケーション内に存在する全てのReducerを1つのReducerとして結合することできます。
src/reducers/index.jsimport { combineReducers } from 'redux'; // このimportが必要 import reducer1 from './reducer1'; import reducer2 from './reducer2'; export default combineReducers({ reducer1, reducer2 });Store
Storeの特徴は以下のことが挙げられます。
- Storeはアプリケーション内で唯一のもの
- アプリケーション内のStateが全て集約されている
- 従来のReactではpropsを目的のコンポーネントまでバケツリレー形式で渡していたが、
<Provider>
によりその必要がなくなる(react-reduxからインポート)Storeは以下のように作成します。
src/index.jsimport {createStore} from 'redux'; import reducer from './reducers'; const store = createStore(reducer);connect関数
ReactとReduxは互いに独立しているため、なにもしないままだとReact ComponentをReduxのフローに乗せることができません。そこでReact Reduxが提供している
connect()
関数を利用して、ReduxのStateやActionとReact Componentを接続します。このとき接続されたComponentはContainerと呼ばれることもあります。このように
connect()
関数によってContainerではStoreから必要なデータ(Stateの一部)と、ActionをStoreに渡すためのdispatch()
関数を使用することができます。
基本的な記述方法としては以下のように書きます。connect(mapStateToProps, mapDispatchToProps)(App);ここで
connect()
関数の引数に渡されているmapStateToProps
とmapDispatchToProps
について詳しく見ていきましょう。mapStateToProps
StoreにあるStateの情報からContainerで必要な情報を取り出し、Container内のpropsとしてマッピングする機能を持つ関数です。
mapState
として呼ばれることもあります。
上記でも示したようにconnect()
関数の第1引数になります。
呼ばれるタイミング Storeの状態が変化したとき 第1引数 State(Storeの全ての状態) 第2引数(オプション) 自身のprops(ownProps) 戻り値 Containerが必要としているStateをpropsとして返す const mapStateToProps = state => { return { // 全体のStateから必要な情報 (state.valueなど) // value: state.value // のように記述 }; };ownPropsを第2引数に設定した場合、最終的な戻り値にはownPropsと新たに得たデータが1つのpropsとしてマージされます。
mapDispatchToProps
ActionをStoreにdispatchする
dispatch()
関数をpropsにマッピングするために使用されます。mapDispatch
として呼ばれることもあります。
dispatch()
関数とは、あるActionが発生したときにReducerにtype
に応じた状態遷移を実行させるための関数です。Storeに標準でdispatch()
関数が用意されています。
mapDispatchToProps
をconnect()
関数の引数に指定しなくてもコンポーネントはデフォルトでprops.dispatch
を受け取れるので、これを使用してStoreにActionをdispatchすることができます。では
mapDispatchToProps
を使用する理由はなんでしょうか?
例として、「ボタンを押すとvalue
が+1される」というContainerを見てみましょう。
mapDispatchToProps
を使用しない場合
Container側でActionCreatorによってActionを作成した段階ではdispatchされていないので以下のように記述する必要があります。import React, { Component } from 'react'; import { connect } from 'react-redux'; import { increment } from '../actions'; class Counter extends Component { render(){ const props = this.props; return ( <React.Fragment> <div>count: {props.value}</div> <button onClick={() => dispatch(increment())}>+1</button> {// ここ } </React.Fragment> ); } } const mapStateToProps = state => ({ value: state.count.value}); export default connect(mapStateToProps /*, 第2引数なし(mapDispatchToProps) */)(Counter);若干JSXの記述が複雑になっています。
mapDispatchToProps
を使用した場合import React, { Component } from 'react'; import { connect } from 'react-redux'; import { increment } from '../actions'; class Counter extends Component { render(){ const props = this.props; return ( <React.Fragment> <div>value: {props.value}</div> <button onClick={props.increment}>+1</button> </React.Fragment> ); } } const mapStateToProps = state => ({ value: state.count.value}); const mapDispatchToProps = ({ increment }); // 上記は下の記述と同じ意味 // const mapDispatchToProps = dispatch => ({ // increment: () => dispatch(increment()) // }); export default connect(mapStateToProps, mapDispatchToProps)(App);このように記述することができます。
mapDispatchToProps
内ではActionCreatorでActionを生成しdispatchまで行う処理を関数に定義してpropsに渡しています。今回は処理が少ない場合でしたのであまり影響はありませんが、Actionの種類や作成の数が多くなった場合には
mapDispatchToProps
を使用することでContainer側で毎回dispatch
処理を記述する必要がなくなります。
呼ばれるタイミング ActionCreatorが呼び出されたとき
(今回はクリックされたとき)第1引数 dispatch()関数 第2引数(オプション) 自身のprops(ownProps) 戻り値 指定したActionのdispatch処理をpropsとして返す 第2引数としてownPropsを設定した場合、Containerが新しいpropsを受け取ったタイミングで
mapDispatchProps
が呼び出されます。Provider
Component階層の最上位のComponentを
<Provider>
でネストすることで全体のComponentをStoreに接続することが可能になります。
それによって任意のComponentでconnect()
関数を用いてStoreと接続できます。コードの記述イメージは以下のようになります。
src/index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { App } from './App'; import createStore from './createReduxStore'; const store = createStore(); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );まとめ
長くなりましたが、Reduxの基礎については以上にしたいと思います。
では簡単に今回の記事の内容をまとめます。
- Redux:アプリケーションの状態管理を行うためのフレームワーク
- Action:アプリケーションでなにが起きたのかを示すオブジェクトデータ
- Reducer:Actionの種類に応じて状態を変化させるメソッド
- Store:アプリケーションの全ての状態を保持している場所
- connect():React ComponentとReduxを接続するためのメソッド
- Provider:connect()でComponentをStoreに接続するために必要なもの
各要素の役割とデータフローをしっかり抑えておくことが重要です。
Reduxについては個人的に理解するのがなかなか大変で、まだ完全には理解できていない面もあるので今後も勉強していきたいと思います。参考資料
- 投稿日:2020-05-24T07:53:56+09:00
React入門 第二章 ~Reactを使ってみよう Todoアプリ~
React入門目次
- 第一章 ~環境構築 Hello React!!~
- 第二章 ~Reactを使ってみよう~
- 第三章 ~Reactでhooksを使おう~
- 第四章 ~ReactでAPI通信をしてみよう(axios使用)~
- 第五章 ~ReactにReduxを組み込もう(hooks仕様)~
- 第六章 ~React × Reduxをtypescriptで書こう(hooks仕様)~
- 第七章 ~React × Redux × typescriptでAPI通信(redux-saga・axios・hooks仕様)
- 番外編 ~atomic designとは~
初めに
Reactを学びたい・使いたいと考えている人・SPA作りたい人向けにReactの基本からreduxやtypescript等の応用を導入するところまで説明していきます。(順次公開していきます)
第二章 ~Reactを使ってみよう~
今回はReactを実際に使ってReactの使い方を説明していきます。
第二章の流れ
- Reactとは
- コンポーネントを作ってみよう
- クラス記法とfunction記法
- todoアプリを作ろう
- 練習問題
1. Reactとは
Reactとはユーザーインターフェイス構築の為のJavaScriptライブラリです。
主にSPA(シングルページアプリケーション)の構築に使われています。
大きな特徴としては3つあります。
1. コンポーネント指向によりUIの高再利用性: 一度作ったUIを他の画面・アプリケーション等で使いまわせる
2. 仮想DOMによるレンダリングコストの削減: 変更箇所の差分検知=>差分箇所の変更をする事で従来の全てを作り直すDOM処理より変更が高速になる。
3. jsx記法: JavaScript内にHTMLを書くような書き方ができる。2. コンポーネントを作ってみよう
まずプロジェクトを作ります。
今回はtodoアプリを作っていくのでプロジェクト名react-todoで作ります。
下記のコマンドでプロジェクトを作ります。create-react-app react_todosrcの配下にcomponentsという名前でフォルダーを作ります。
componentsにTodo.jsxというファイルを作ります。コード内容の説明
constructor内でstateを初期化しています。
returnでtodoを表示するpタグを作っています。
export default TodoでTodoコンポーネントを別ファイルでimportできるようにしています。src/components/Todo.jsximport React from "react"; class Todo extends React.Component{ constructor(props){ super(); this.state = { todo: "react勉強中" } } render(){ return( <div> <p>{this.state.todo}</p> </div> ); } } export default Todo;App.jsでTodo.jsxをimportし、今作ったTodoコンポーネントを反映させます。
src/App.jsimport React from 'react'; import logo from './logo.svg'; import './App.css'; import Todo from './components/Todo' function App() { return ( <div className="App"> <Todo/> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;では結果を見ます。
下記コマンドで起動してください。npm start結果画面
3. クラス記法とfunction記法
Reactにはいくつか書き方があります。
classでコンポーネントを作る記法とfunctionでコンポーネントを作る記法です。先ほど作ったコンポーネントはclass記法で書いていましたが、
function記法でも同じ結果を作ることができます。下記のようにTodo.jsxを書き直してみてください。
function記法とhooksを使った書き方です。src/components/Todo.jsximport React, {useEffect, useState} from 'react'; /* class Todo extends React.Component{ constructor(props){ super(); this.state = { todo: "react勉強中" } } render(){ return( <div> <p>{this.state.todo}</p> </div> ); } } */ const Todo = (props) =>{ const[todo, setTodo] = useState([]); useEffect(() => { setTodo("react勉強中 function") },[]) return( <div> <p>{todo}</p> </div> ); } export default Todo;ではどちらを使った方がいいのかという疑問が湧くと思います。
個人的にはfunction記法をお勧めします。よく知りたいという方は下記URL等でご確認ください。
https://overreacted.io/ja/how-are-function-components-different-from-classes/最初は記法が何種類かあるんだなと思ってもらい自分と違う記法を見ても驚かないようにしておけば大丈夫だと思います。
今回はclassコンポーネントで作っていきます。4. todoアプリを作ろう
まずはAppjsxのファイルの一つのコンポーネントだけでTodoアプリを作ります。
App.jsxを下記のコードに書き換えます。src/App.jsimport React from 'react'; import logo from './logo.svg'; import './App.css'; import Todo from './components/Todo' class App extends React.Component { constructor(props){ super(); this.state = { todoList: ["React勉強", "todoアプリ作成"] } this.handleAdd = this.handleAdd.bind(this); } handleAdd(e){ e.preventDefault(); this.state.todoList.push(e.target.todo.value); this.setState(this.state.todoList); e.target.todo.value = ''; } render(){ return( <div> <form onSubmit={this.handleAdd}> <input type="text" name="todo"/> <input type="submit" value="追加"/> </form> <ul> {this.state.todoList.map( (item, i) => { return<li key={i}>{item}</li> } )} </ul> </div> ); } } export default App;結果
textBoxの中にtodoを入力して追加ボタンを押しtodoを追加してみてください。
これでTodoアプリ自体は完成ですが、これはReactらしい書き方では無くReactを活かせていません。
なぜなら、コンポーネント指向で作っていないからです。ではコンポーネントを分けて作っていきます。
今回はForm部分とList部分でそれぞれコンポーネントを作りAppコンポーネントで合わせる方法でやります。まず、componentsフォルダにForm.jsxとTodoList.jsxを作ってください。
それぞれのファイルに下記のコードを書いてください。
components/Form.jsximport React from 'react'; class Form extends React.Component { constructor(props) { super(props); this.state = { input: '' }; } handleChange = e => { this.setState({ input: e.currentTarget.value }) }; handleSubmit = e => { e.preventDefault(); this.props.onSubmit(this.state.input); this.setState({ input: '' }) } render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.input} onChange={this.handleChange} /> <button>追加</button> </form> ); } } export default Form;components/TodoList.jsximport React from 'react'; class TodoList extends React.Component { render() { const {todoList} = this.props return ( <div> <ul> {todoList.map( (item, i) => { return<li key={i}>{item}</li> } )} </ul> </div> ) } } export default TodoList;App.jsxを書き変えます。
App.jsximport React from 'react'; import logo from './logo.svg'; import './App.css'; import Todo from './components/Todo' import Form from './components/Form'; import TodoList from './components/TodoList'; class App extends React.Component { constructor(props){ super(); this.state = { todoList: ["React勉強", "todoアプリ作成"] } this.handleAdd = this.handleAdd.bind(this); } handleAdd(value){ this.setState({todoList: this.state.todoList.concat(value)}); } render(){ return( <div> <Form onSubmit={this.handleAdd}/> <TodoList todoList={this.state.todoList}/> </div> ); } } export default App;propsで状態ををバケツリレーしているイメージですね。
コンポーネントの分け方はいろいろな分け方できるので状況に応じてコンポーネントの切り分けをしてください。
例)今回はtodoListとFormで分けましたが、TodoListをさらにtodoとListに分けることやFormをさらにTextBoxとButtonのコンポーネントとして分けることもできます。それではReact練習として今作成したTodoアプリにtodoの削除機能をつけてみてください。
ヒント 1:各Todoに削除ボタンを作り、onClickメソッドに削除機能をを入れます。
ヒント 2:TodoListの各Todoに削除で使う為のidを追加します。答え:
下記のように各ファイルを変更します。components/TodoList.jsximport React from 'react'; class TodoList extends React.Component { handleDelete = e => { const id = e.target.value; const {onDelete} = this.props; onDelete(id); } render() { const {todoList} = this.props return ( <div> <ul> {todoList.map( (item) => { return<li key={item.id}>{item.content}<button value={item.id} onClick={this.handleDelete}>削除</button></li> } )} </ul> </div> ) } } export default TodoList;App.jsximport React from 'react'; import logo from './logo.svg'; import './App.css'; import Todo from './components/Todo' import Form from './components/Form'; import TodoList from './components/TodoList'; class App extends React.Component { constructor(props){ super(); this.state = { id: 3, todoList: [{id: 1, content: "React勉強"},{id: 2, content: "todoアプリ作成"}], } this.handleAdd = this.handleAdd.bind(this); } handleAdd(value){ this.setState({todoList: this.state.todoList.concat({id: this.state.id, content: value})}); this.state.id++; } handleClickDelete = id => { console.log(id); this.setState({ todoList: this.state.todoList.filter(todo => todo.id != id) }); }; render(){ return( <div> <Form onSubmit={this.handleAdd}/> <TodoList todoList={this.state.todoList} onDelete={this.handleClickDelete}/> </div> ); } } export default App;これで第2章は終わりです。
次回は第三章 ~Reactでhooksを使おう~です。第三章以降はfunction記法で説明していきます。
- 投稿日:2020-05-24T07:53:20+09:00
React入門 第一章 ~環境構築 Hello React!!~
React入門目次
- 第一章 ~環境構築 Hello React!!~
- 第二章 ~Reactを使ってみよう~
- 第三章 ~Reactでhooksを使おう~
- 第四章 ~ReactでAPI通信をしてみよう(axios使用)~
- 第五章 ~ReactにReduxを組み込もう(hooks仕様)~
- 第六章 ~React × Reduxをtypescriptで書こう(hooks仕様)~
- 第七章 ~React × Redux × typescriptでAPI通信(redux-saga・axios・hooks仕様)
- 番外編 ~atomic designとは~
初めに
Reactを学びたい・使いたいと考えている人・SPA作りたい人向けにReactの基本からreduxやtypescript等の応用を導入するところまで説明していきます。(順次公開していきます)
第一章 ~環境構築~
今回第一章では開発環境を作っていきましょう
第一章の流れ
- node.jsをインストールしよう
- パッケージマネージャーの確認
- creat-react-appをインストールしよう
- VSCodeをインストールしよう
- reactを起動しよう
- Hello React!!
1.node.jsをインストールしよう
node.jsはReactを使う際に必須なのでインストールします。
まず、https://nodejs.org/ja/ に行きLTS版をダウンロード
ダウンロード・インストール共に終わったらインストールの確認をします。
バージョンが出てればOK!!node -v v12.16.32.パッケージマネージャーの確認
パッケージ管理をしてくれるシステムです。
npmやyarn等種類はあるのですが今回はnode.jsと一緒にインストールされているはずのnpmを利用していきます。npmが入っているかの確認
バージョンが出ればOK!!npm -v 6.13.4どうしてもyarnが使いたいという人は
下のコマンドでインストールとインストール確認をしてください。(上記のようにnpmを使う人は飛ばしてください)
バージョンが出ればOKですnpm install -g yarn yarn -v3.creat-react-appをインストールしよう
creat-react-appとはReactで使う基本的なパッケージをまとめて簡単にインストールしてくれるものです。
(Webpack, Babel ,ESLint等を個別でインストールしなくていいよって感じです。)下記のコマンドでインストールしてください。
npm install -g create-react-app
4.VSCodeをインストールしよう
VSCodeはエディターです。
エディターに強いこだわりがある人以外はVSCodeをインストールしてください。
Reactの開発にはVSCodeが本当に使いやすいです。下記リンクからダウンロードしてください。
https://code.visualstudio.com/5.reactを起動しよう
まずは下記のコマンドでアプリケーションを作りましょう。
create-react-appの後ろはプロジェクト名を書きます。今回はhello_reactにします。コンソールの最後にHappy hacking!が出れば成功です
create-react-app hello_react次に作成したプロジェクトに移動し、起動します。
cd hello_react npm start6.Hello React!!
では第一章の最後
実際にReactを見て書いていこうまず、VSCodeを開いてください。
Open Folderをクリックし先ほど作成したプロジェクトを開いてください。
開いたらsrcフォルダー内のApp.jsxを開いてください。
少し解説するとreturn()内部に記述されている箇所が現在ブラウザに表示されているコードです。src/App.jsximport React from 'react'; import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;このreturn()中のコードを変えてHello React!!を表示させます。
下記のようにApp.jsxを変えてください。src/App.jsximport React from 'react'; import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <p>Hello React!!</p> </div> ); } export default App;お疲れ様でした!!
これで第一章 ~環境構築 Hello React!!~は終了です。次回は第二章 ~Reactを使ってみよう~です。
- 投稿日:2020-05-24T03:31:02+09:00
create-react-app で .eslintignore を有効にする
WHY?
.eslintignore に定義されたファイルとディレクトリはESLintの検査対象外となります。
通常はrootディレクトリに配置するだけでOKですが、
create-react-appで作成した場合はrootディレクトリに配置しただけでは有効になりません。手順
package.jsonと同じところに.envファイルを用意して以下を定義ます。
.envEXTEND_ESLINT=true以上です。
こちらに載ってました。
Enable .eslintignore with extend flag (#7562)おまけ
create-react-appの中はこんな感じです。
packages/react-scripts/config/webpack.config.js{ ignore: process.env.EXTEND_ESLINT === 'true', }
- 投稿日:2020-05-24T03:31:02+09:00
create-react-appで.eslintignore を有効にする
WHY?
.eslintignore に定義されたファイルとディレクトリはESLintの検査対象外となります。
通常はrootディレクトリに配置するだけでOKですが、
create-react-appで作成した場合はrootディレクトリに配置しただけでは有効になりません。手順
package.jsonと同じところに.envファイルを用意して以下を定義ます。
.envEXTEND_ESLINT=true以上です。
こちらに載ってました。
Enable .eslintignore with extend flag (#7562)おまけ
create-react-appの中はこんな感じです。
packages/react-scripts/config/webpack.config.js{ ignore: process.env.EXTEND_ESLINT === 'true', }
- 投稿日:2020-05-24T01:44:06+09:00
React Typescript (.tsx)で 郵便番号フォームを作る。
はじめに
こんにちは
最近は、もっぱらGo Nuxtで作ったポートフォリオサイトをブラッシュアップし続けてそこら辺もだいぶ収集がついてきたので、
新しいことをやろうと以前から目をつけていたReact(Typescript込み)をやろうと思い、実際に公式のチュートリアルをやってみたところ楽しくてハマってしまい、こりゃなんか作ろうか、ということで郵便番号を受け取って自治体情報を返すアプリを作っておりました。しかし、なんか無料公開されている郵便番号APIがしっくり来るものが見当たらなかったため(そんなに探してない)来たるべき時代までGithubに冷凍保存しておこうかと、そして、そのついでにコードの鎮魂のために郵便番号を受け取るフォーム部分を記事に書いておこうかなということで投稿します。
ちなみに、構築はローカルにcreate-react-app [アプリ名] --typescriptコマンドです。
※この記事の作者?React=初心者&typescript=仕事でちょっとだけ通り過ぎただけ
※郵便番号を受け取るフォームコンポーネント部分だけを書いていきます。本編
まず、郵便番号を受け取るフォームに必要な要素がなにかを考えました。
[課題]
1. 前後の空白を削除する。
2. 全角で入力されたら、そのまま全角で受け取りたく無い->半角にする。
3. 3文字目と4文字目の間にハイフンを入れてくる人もいる。->ハイフンを消す?
4. 7桁の数字を文字列で受け取る。-> 7桁以外ならボタンをdisableにする?おまけ
- 日本語表記と英語表記を切り替えたい
まずコンポーネントのファイルをForm.tsxとかで作成します。
App.tsx ------> Home.tsx ------> Form.tsx の流れでpropsを渡します。
API叩く処理は割愛します。一個上のHome.tsxはこんな感じです。
Home.tsximport React, { FC } from 'react' import Form from '../components/Form' import * as w from '../words.json' // App から flag を受け取っています。 type Props = { flag: boolean } const postForm = (value: string): void => { // axiosなどでAPI叩く? } const Home: FC<Props> = props => { // 日本語か英語科の分岐 const btnstr: string = props.flag ? w.btnstr_en : w.btnstr const placeholder: string = props.flag ? w.placeholder_en : w.placeholder return ( <React.Fragment> <Form postForm={postForm} btntext={btnstr} placeholder={placeholder} /> <style>{` `}</style> </React.Fragment> ) } export default Homeおまけ要素の英語との切り替えのためにAppからflagをもらってきてる以外は普通です。
flagによって英語と日本語を切り替えます。(おまけクリアー)Formコンポーネントにpropsとしてbtntext(ボタンに表示するテキスト)とplaceholder(input内に表示するテキスト)とボタンを押したときの関数postFormを渡しているだけです。
ちなみにwords.jsonからテキストをひっぱってきているので載せておきます。
words.json{ "btnstr": "検索", "placeholder": "郵便番号入力", "unsuitable": "文字数が適切ではありません", "isnumber": "値には数値を入力してください", "btnstr_en": "Click Me", "placeholder_en": "Please, input zipcode", "unsuitable_en": "The number of characters is incorrect", "isnumber_en": "Please, input a number" }そんで、本題のForm.tsxです。
Form.tsx... const [value, setValue] = useState('') ... <input type="text" ... // setValueで更新されたvalueが入る。 value={ value } onBlur={(e) => { let newVal: string = ConvertToHS(e.target.value) newVal = OptStr(newVal) // React Hooksでstate更新 setValue(newVal) }} />inputからいきます。受け取った値をチェックしたいです。
onBlurをみてください。
- [課題]1,3: 前後の空白カット。, ハイフンの除去 -> OptStr
- [課題]2: 全角を半角にする。 -> ConvertToHs
という外部関数で解消します。
1の方はハイフンを取り去って前後の空白を除去、
2の方は単に正規表現で全角->半角の置き換えをしているだけです。export const OptStr = (str: string): string => { let returnStr: string = '' const slice: Array<string> = str.split('-') returnStr = slice.join('') return returnStr.trim() } export const ConvertToHS = (str: string): string => { return str.replace(/[A-Za-z0-9]/g, function (s) { return String.fromCharCode(s.charCodeAt(0) - 0xfee0) }) }これらの受けとった値を整形する1~3の課題はフォーカスが外れたときなどに実行したいのでOnBlurにしました。
[課題]4のdisableもやっちゃいましょう。
しかも、なんならついでにvalidationもクライアントの方である程度したい。Form.tsxconst unsuitable: string = props.flag ? w.unsuitable_en : w.unsuitable const isnumber: string = props.flag ? w.isnumber_en : w.isnumber const [value, setValue] = useState('') const [validatemessage, setValidatemessage] = useState('') ... <input ... value={value} placeholder={props.placeholder} autoComplete="postal-code" onChange={(e) => { setValue(e.target.value) !( (e.target.value.indexOf('-') === -1 && e.target.value.length === 7) || (e.target.value.indexOf('-') !== -1 && e.target.value.length === 8) ) ? setValidatemessage(unsuitable) : setValidatemessage('') }} onBlur={(e) => { let newVal: string = ConvertToHS(e.target.value) newVal = OptStr(newVal) setValue(newVal) newVal && newVal.length !== 7 ? setValidatemessage(unsuitable) : setValidatemessage('') }} /> <button disabled={ !( (value.indexOf('-') === -1 && value.length === 7) || (value.indexOf('-') !== -1 && value.length === 8) ) } onClick={async () => { if (!isNaN(Number(value))) { value.length === 7 ? await props.postForm(value) : await setValidatemessage(unsuitable) } else { await setValidatemessage(isnumber) await setValue("") } }} > {props.btntext} </button>onChangeでReactHooksを使ってvalidationの更新を行っていきます。
そのときにハイフンが入っているときはハイフン含めて8文字になりますが、受け取れるようにしています。buttonの中のdisabledにもハイフンが入っている場合は8文字を受け取る処理を書いて、
クリックしたときに
if (isNan(...)) {}
で数字かどうかを判定して入力が不適切ならvalidationにいれます。([課題]4クリアー)煩雑になりましたが、以上です。
最後に完成版載せておきます。
Form.tsximport React, { FC, useState } from 'react' import { OptStr, ConvertToHS } from 'react-app-env.d' import * as w from '../words.json' type Props = { flag: boolean btntext: string placeholder: string postForm: (value: string) => void } const Form: FC<Props> = (props) => { // 日本語と英語の分岐 const unsuitable: string = props.flag ? w.unsuitable_en : w.unsuitable const isnumber: string = props.flag ? w.isnumber_en : w.isnumber // React Hooks でstateを更新するかー const [value, setValue] = useState('') const [validatemessage, setValidatemessage] = useState('') return ( <React.Fragment> <div className="form"> <div className="form__input__wrapper"> <p className="validate">{validatemessage}</p> <input type="text" value={value} className="form__input" placeholder={props.placeholder} autoComplete="postal-code" onChange={(e) => { setValue(e.target.value) !( (e.target.value.indexOf('-') === -1 && e.target.value.length === 7) || (e.target.value.indexOf('-') !== -1 && e.target.value.length === 8) ) ? setValidatemessage(unsuitable) : setValidatemessage('') }} onBlur={(e) => { let newVal: string = ConvertToHS(e.target.value) newVal = OptStr(newVal) setValue(newVal) newVal && newVal.length !== 7 ? setValidatemessage(unsuitable) : setValidatemessage('') }} /> </div> <button className={ !( (value.indexOf('-') === -1 && value.length === 7) || (value.indexOf('-') !== -1 && value.length === 8) ) ? 'disable form__btn' : 'form__btn' } disabled={ !( (value.indexOf('-') === -1 && value.length === 7) || (value.indexOf('-') !== -1 && value.length === 8) ) } onClick={async () => { if (!isNaN(Number(value))) { value.length === 7 ? await props.postForm(value) : await setValidatemessage(unsuitable) } else { await setValidatemessage(isnumber) await setValue("") } }} > {props.btntext} </button> </div> <style>{` .validate { color: red; font-size: 10px; position: absolute; top: 0; left: 15px; } .sentence { text-align: center; } .form { display: flex; justify-content: center; height: 80vh; padding: 34vh; } .form__input__wrapper { position: relative; } .form__input { width: 300px; height: 40px; outline: none; border-radius: 5px; border: solid 3px black; padding: 0 10px; margin: 15px; } .form__btn { cursor: pointer; padding: 0 10px; border: solid 3px black; width: 74px; height: 40px; margin: 15px; border-radius: 5px; outline: none; background-color: black; color: white; font-weight: bold; } .disable { opacity: 0.6; } `}</style> </React.Fragment> ) } export default Form最後に
見ていただいた方ありがとうございます。
Qiitaも2記事目なので、うまいこと要点をまとめられませんでしたが、これで件のコードもおとなしく眠ってくれると思います。最後に開発途中のスクショ貼っておきます。
では、また!!