- 投稿日:2020-11-21T23:37:49+09:00
create-react-app×TypeScriptでTypeError: Cannot assign to read only property 'jsx' of object '#<Object>'のエラーが出る
はじめに
create-react-appでTypeScriptのプロジェクトを始めた時に、yarn startすると、
TypeError: Cannot assign to read only property 'jsx' of object '#'が出たので解決策を記しておきます内容
$ npx create-react-app sample --template typescript $ cd sample $ yarn startでreactのプロジェクトを開始しようとした時に、
/Users/yourname/sample/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js:239 appTsConfig.compilerOptions[option] = value; ^ TypeError: Cannot assign to read only property 'jsx' of object '#<Object>' at verifyTypeScriptSetup (/Users/yourname/sample/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js:239:43) at Object.<anonymous> (/Users/yourname/sample/node_modules/react-scripts/scripts/start.js:31:1) at Module._compile (internal/modules/cjs/loader.js:1137:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10) at Module.load (internal/modules/cjs/loader.js:985:32) at Function.Module._load (internal/modules/cjs/loader.js:878:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12) at internal/main/run_main_module.js:17:47 error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.のエラーが出ました。
解決策としましては、
$ yarn add typescript@4.0.5そして、以下の変更を加えます。
tsconfig.json- "jsx": "react-jsx" + "jsx": "react"これで動くはずです!
なお、詳しくは https://github.com/facebook/create-react-app/issues/10110 で書かれているので気になる方は読んでみてください
- 投稿日:2020-11-21T22:50:55+09:00
Reactでmap型の要素をループしてテーブルを作成する
コード
- 要素数的なものは取れないのかな、、、
- tableはテーブル全体
- thはテーブルの見出し(table head cell)
- tdはテーブル内のデータ(table data)
- trはテーブルの横の定義(table row)
- アロー関数のループの中でテーブルを定義する感じ
<div> <table border="1" width="500" cellPadding="0"> <th>No</th> <th>Name</th> <th>Status</th> {this.state.pods.map((pod) => <tr> <td>{pod.id}</td> <td>{pod.metadata.name}</td> <td>{pod.status.phase}</td> </tr> )} </table> </div>画面
- 投稿日:2020-11-21T22:36:49+09:00
create-react-appコマンド無しでcreate-react-appっぽい環境をつくる
タイトル通り create-react-appコマンド無しでcreate-react-appっぽいreact + typescriptの環境構築していきます。 今回、create-react-appぽいとは次のようなことを指します。
- とりあえずts,tsxが動く
- yarn buildでbuildファイルを生成する。
- yarn startでローカルサーバーが自動で起動する。
- ローカルサーバーが起動中、編集すると、自動でリロードされる(ホットリロード)
登場人物ややこしすぎ問題
とにかく構築にいろいろなパターンが多い + デファクトがよくわからない。1
本記事ではwebpack + ts-loaderの構成で構築していこうと思います。(babel-loaderを使わないパターンです)
そのため、対象となる読者は以下のとおりです。・始めたてで、とにかく動かしてみたいだけ。 ・細かいpolyfillは考えていない。 ・作るものが小規模だ。(個人の練習アプリ程度)環境
環境は以下の通りです。yarn使います。
$ node -v v13.11.0 $ yarn -v 1.22.4init
まずはじめにpackage.jsonを生成します。
$ yarn init -y //-yで対話をスキップパッケージのインストール
次にwebpackともろもろのインストールを行います。
$ yarn add -D webpack@4.43.0 webpack-cli@3.3.12 webpack-dev-server@3.11.0 html-webpack-plugin typescript ts-loader正常な動作を確認しているのは上記のバージョンのwebpack,webpack-cli,webpack-dev-serverのみです。バージョンを指定しない場合webpack v5系がインストールされるので注意してください。(2020年11/21日現在)2
- webpack ・・・・みんな大好きモジュールバンドラーの本体
- webpack-cli ・・・・webpackコマンドがshellで使える様になる。package.jsonのscriptsにこのコマンドを割り当てることが多い
- webpack-dev-server ・・・・開発サーバー用
- html-webpack-plugin ・・・・必須ではないです。webpackがバンドルと一緒に.htmlも吐き出してくれます。自前でhtmlファイルを用意してもいいですが、使い方がとても簡単かつ楽なので筆者はいつもwebpackにまかせてます。
- typescript ・・・・いつものあいつ
- ts-loader ・・・・webpackがバンドルを作成するときにtsx—> jsx —> jsとコンパイルしてくれます。babel-loaderとごっちゃになってハゲそうになった。
scriptsの設定
先にpackage.jsonにscriptsを設定しておきましょう。
"scripts":{ // dev-serverをdevelopmentモードで起動 "start": "webpack-dev-server --config ./webpack.config.js --mode development”, // productionモードでbuildファイルの出力 "build":"webpack --mode production" }reactのインストール
次にreactと型定義ファイルをインストールします。こちらの詳細は割愛します。
$ yarn add react react-dom $ yarn add -D @types/react @types/react-domここから諸々のファイルを作成していきます。最終的なディレクトリ構成を載せておきます。
├── build //buildディレクトリ │ ├── bundle.js │ └── index.html ├── package.json ├── src │ ├── index.tsx //エントリーポイント │ └── template // htmlpluginのテンプレート │ └── index.html ├── tsconfig.json ├── webpack.config.js └── yarn.lockwebpack.config.js
webpack.config.jsを作成します。
// webpack.config.js const path = require("path"); //htmlプラグインのための呪文 const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", entry: { bundle: "./src/index.tsx", // エントリーファイルの位置 }, output: { filename: "[name].js", // 今回はbundle.jsで吐き出される(entryで指定) path: path.join(__dirname, "build"), // buildという名前のbuildディレクトリを吐き出す chunkFilename: "[name].js",//今回は触れません }, devServer: { contentBase: path.join(__dirname, "build"), compress: true, port: 8080, open: true, //yarn start コマンドを入力すると自動でブラウザに遷移します。 historyApiFallback: true, }, module: { rules: [ { test: /\.ts(x?)$/, //.ts または.tsxに対して loader: "ts-loader", // ts-loaderを使用 }, ], }, resolve: { extensions: [".ts", ".js", ".tsx", ".jsx"], }, plugins: [ new HtmlWebpackPlugin({ template: `${__dirname}/src/template/index.html`, //テンプレートファイルの場所 filename: "index.html", //テンプレートファイルの名前 inject: "body", //bodyタグの直前でwebpackのバンドルを埋め込み }), ], };おまけ: 本当はwebpackのprodモードとdevモードの環境は個別に設定することが推奨されています。詳しくは公式ドキュメントを参照してください。今回はscriptsで無理やり分けてます。
tsconfig.json
次にtsconfig.jsonを作成します。ts-loaderはこのファイルの設定に従います。
必ず "jsx":"react"を指定しましょう。
ほぼデフォルトのコピペです。細かいのはお好みで。// tsconfig.json { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "resolveJsonModule": true, /* Basic Options */ "allowJs": false /* Allow javascript files to be compiled. */, "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, "sourceMap": true /* Generates corresponding '.map' file. */, "outDir": "/build" /* Redirect output structure to the directory. */, "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, /* Strict Type-Checking Options */ "strict": true /* Enable all strict type-checking options. */, "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "strictNullChecks": true /* Enable strict null checks. */, /* Additional Checks */ "noUnusedLocals": true /* Report errors on unused locals. */, "noUnusedParameters": true /* Report errors on unused parameters. */, "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, /* Module Resolution Options */ "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, relative to the 'baseUrl'. */ structure of the project at runtime. */ "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, /* Advanced Options */ "skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, "include": ["src"] }仕上げ
.tsxとhtmlのテンプレートを作成します。
// src/index.tsx import React from "react"; import ReactDOM from "react-dom"; ReactDOM.render(<h1>hello</h1>, document.getElementById("root"));// src/template/index.html // ! + tab キーで作ってます。 // <div id = "root"></div>を忘れずに <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id='root'></div> </body> </html>ここで、
$ yarn startするとindex.tsxの内容が描画されると思います。
ソースコードはこちら
- 投稿日:2020-11-21T17:47:19+09:00
React入門 1週間でReactをマスターする #04. 条件分岐(if)と繰り返し(loop)
目次
1. Reactの新規プロジェクトの立ち上げ
2. コンポーネントの書き方とイベントハンドラ
3.Class Components と Function Components
4. 条件分岐 (if) と繰り返し (loop) ←今ここ
5. フォームと親子間のデータのやり取り (近日公開!)
6. コンポーネントのライフサイクル (準備中)
7. スタイル (準備中)
8. Higher-Order Component (準備中)
9. Portalを利用したモーダル (準備中)
10. refによるエレメントの取得 (準備中)
11. Contextを利用したテーマの変更 (準備中)今回の学習内容
今回は、
- JSX内での条件分岐(状態によっての表示切り替え)
- JSX内での項目の繰り返し表示
をやっていきます。
YouTubeでの解説動画
YouTubeでも解説しています。
動画で確認したい方はこちらもどうぞ。
【YouTube動画】 未経験から1週間でをマスターするReact入門 #04. React入門 1週間でReactをマスターする #04. 条件分岐(if)と繰り返し(loop)
この記事のソースコード
ソースコードはGitHubで公開しています。
https://github.com/yassun-youtube/ReactTutorial
今回は下記コミットをやっていきます。
タブによる条件分岐
今回は、タブを作って、そのどのタブを表示しているかを状態で管理しておき、条件分岐をやってみます。
タブで
List.js
とForm.js
の表示で入れ替えるようにしたいので、Form.js
を作成します。src/Form.jsexport const Form = () => { return ( <div> フォームです </div> ) }これを
App.js
に追加します。src/App.jsimport { useState } from 'react'; import { List } from "./List"; import { Form } from "./Form"; // 追加 function App() { const [description, setDescription] = useState('クリック前の表示'); const changeDescription = () => { setDescription('クリック後の表示です。') } return ( <div> { description } <List title="取り扱い言語一覧"/> <Form /> { /* 追加 */ } <button onClick={changeDescription}>ボタン</button> </div> ); } export default App;これで下図のように表示されます。
続いて
App.js
にヘッダ部分を追加します。デザインをまだ当てていないのでタブのようには見えませんが、タブとして扱います。
src/App.jsimport { useState } from 'react'; import { List } from "./List"; import { Form } from "./Form"; function App() { const [description, setDescription] = useState('クリック前の表示'); const changeDescription = () => { setDescription('クリック後の表示です。') } return ( <div> <header> { /* 追加 */ } <ul> <li>リスト</li> <li>フォーム</li> </ul> </header> <hr /> { // 追加 } { description } <List title="取り扱い言語一覧"/> <Form /> <button onClick={changeDescription}>ボタン</button> </div> ); } export default App;これで下図のように表示されます。
この
リスト
フォーム
というのをタブとみなして、List.js
とForm.js
の表示の状態を管理するようにしてみましょう。src/App.jsimport { useState } from 'react'; import { List } from "./List"; import { Form } from "./Form"; function App() { const [description, setDescription] = useState('クリック前の表示'); const [tab, setTab] = useState('list'); // 追加 const changeDescription = () => { setDescription('クリック後の表示です。') } return ( <div> <header> <ul> <li onClick={() => setTab('list')}>リスト</li> <li onClick={() => setTab('form')}>フォーム</li> </ul> </header> <hr /> { description } { // 変更 tab === 'list' ? <List title="取り扱い言語一覧"/> : <Form /> } <button onClick={changeDescription}>ボタン</button> </div> ); } export default App;この変更で、ヘッダのリストの項目をクリックすることで内容が変わるようになりました。
コード解説
コードの内容を見ていきましょう。
const [tab, setTab] = useState('list'); // 追加新しく、こちらのコードを追加しました。
これは、
tab
という state を定義しています。 初期値はlist
です。{ // 変更 tab === 'list' ? <List title="取り扱い言語一覧"/> : <Form /> }次にこちらの部分で表示を制御しています。
JSXでは、
{}
は Javascript の値を入れられます。ここではその中で三項演算子を使ってtab
の状態で表示を分岐させていることがわかります。Reactでは、このように条件分岐で画面の表示を変更することが可能です。
繰り返しによるリスト表示
次に、Reactによる繰り返しを見ていきます。
List.js
に取り扱い言語のリストとなるステートを用意して、それをリスト表示させてみましょう。src/List.jsconst LANGUAGES = [ // 追加 'JavaScript', 'C++', 'Ruby', 'Java', 'PHP', 'Go' ]; export const List = ({ title }) => { return ( <div> <h4>{ title }</h4> { // 変更 LANGUAGES.map((lang, index) => { return <div key={index}>{ lang }</div> }) } </div> ) }いくつかコードを追加してみました。
こちらのコードにより、下図のように表示が変わります。
コード解説
const LANGUAGES = [ // 追加 'JavaScript', 'C++', 'Ruby', 'Java', 'PHP', 'Go' ];まず、言語リストの初期値として定数を定義しています。
{ LANGUAGES.map((lang, index) => { return <div key={index}>{ lang }</div> }) }こちらのコードが繰り返しの部分です。
ここでは、
map
関数を使って<div>
の配列を返しています。
Reactでは、繰り返しは基本的にmap
などの配列を返す関数を利用して行います。繰り返しのTips
一つ注意しないといけないのは、繰り返しを行う際は、 繰り返される要素にkey
属性を指定する必要があります。これは、Reactが繰り返された要素を区別するために必要なものなので、ユニークである必要があります。
無くても動きますが、コンソールにエラーが出ますし、予測不能なバグを生む可能性があるのでつけるようにしてください。条件分岐、繰り返しまとめ
JSXでは、HTML(のようなもの)の中にコードが記載できるので、そこに 三項演算子や
map
などを利用して要素を返してやれば、条件分岐や繰り返しが可能になります。JavaScriptを理解されている方なら、簡単に条件分岐や繰り返しが可能ですね。
次に向けた余計なコードの整理など
前回、
App.js
中で利用したdescrption
という state は、今後不要なので削除しておきます。また、
title
をList.js
にprops
として渡していますが、こちらも不要なので削除します。src/App.jsimport { useState } from 'react'; import { List } from "./List"; import { Form } from "./Form"; function App() { // descriptionを削除 const [tab, setTab] = useState('list'); return ( <div> <header> <ul> <li onClick={() => setTab('list')}>リスト</li> <li onClick={() => setTab('form')}>フォーム</li> </ul> </header> <hr /> { /* descriptionを削除 */ } { tab === 'list' ? <List /> : <Form /> // Listへのtitle属性を削除 } { /* buttonを削除 */ } </div> ); } export default App;
App.js
からdescription
に関連するコードとList.js
へのtitle
属性を削除しました。次に、
List.js
からtitle
属性を削除します。List.jsconst LANGUAGES = [ 'JavaScript', 'C++', 'Ruby', 'Java', 'PHP', 'Go' ]; export const List = () => { // title を削除 return ( <div> { /* h4 を削除(title表示) */ } { LANGUAGES.map((lang, index) => { return <div key={index}>{ lang }</div> }) } </div> )これできれいになりました。
この状態で入門5に行きたいと思います。
今日やったこと
条件分岐による表示切り替え
条件分岐は、三項演算子を使うことで表現できました。
function Test() { return ( <div> { tab === 'list' ? <List /> : <Form /> } </div> ) }こちらも可↓
let
とif
を使って入れる値を変えるというやり方でもできます。function Test() { const tabComponent = tab === 'list' ? <List /> : <Form /> return ( <div> { tabComponent } </div> ) }繰り返しによるリスト表示
繰り返しは
map
関数を使って表現しました。function Test() { return ( <div> { list.map((val, i) => { return <div key={i}>{ val }</div> }) } </div> ) }Class Componentsへの反映
ここからは、上記の内容を Class Componentsに適用していきます。
ブランチを
class-components
ブランチに切り替えて、 Class Components で今回の最終形の状態に持っていきます。git checkout class-componentsClass Componentsへの反映
src/App.jsimport React from 'react'; import { List } from "./List"; import { Form } from "./Form"; class App extends React.Component { constructor(props) { super(props); this.state = { tab: 'list' }; } render() { const { tab } = this.state; return ( <div> <header> <ul> <li onClick={() => this.setState({ tab: 'list' })}>リスト</li> <li onClick={() => this.setState({ tab: 'form' })}>フォーム</li> </ul> </header> <hr /> { tab === 'list' ? <List /> : <Form /> } </div> ) } } export default App;src/List.jsimport React from 'react'; const LANGUAGES = [ 'JavaScript', 'C++', 'Ruby', 'Java', 'PHP', 'Go' ]; export class List extends React.Component { render() { return ( <div> { LANGUAGES.map((lang, index) => { return <div key={index}>{ lang }</div> }) } </div> ) } }src/Form.jsimport React from 'react'; export class Form extends React.Component { render() { return ( <div>フォームです</div> ) } }これでClass Component側にも反映できました。
同じことをClass Components側にもやることで、復習になります。おわりに
今日は条件分岐と繰り返しについて学びました。
次は、フォームと親子間のデータのやり取りについてお話していきます。
- 投稿日:2020-11-21T13:10:45+09:00
【React,useMemo】今日の日付の計算でuseMemoを使用したら無駄な計算を減らせました。
useMemoの活用
useMemo使ったとき感動したので皆様に共有したくて書きました!
作成中のアプリ
筋トレの種類と、その筋トレを回数を記録するアプリ。
UIはこんな感じで、左上の入力フォームに筋トレの種類(腕立て、スクワットなど)を入力して送信すると、コンポーネントが追加されていくようになっています。
未実装ですが、そのコンポーネントの中のフォームで日付と回数を入力して送信すると、その下に記録が溜まっていくイメージです。(UIはこれが最適かどうかはさておき...)
inputタグの初期値を本日の日付にしたいが再計算が多くなる...
筋トレ記録アプリの作成の中で、日付と筋トレ回数を入力するフォームを作成した。入力するタイミングとして当日中(筋トレ直後)に回数を記録することが多いと思われるため、日付の入力フォームの初期値を当日の日付にしたいと思ったのですが、普通に書くと再計算が多くなり無駄が発生してしまいました...
useMemo無し
useMemo無しだと、筋トレの種類を追加するたびに、別ディレクトリに書いたtoday.tsのcalculateToday関数が余計に実行されてしまいます。
●コード
import React , { FC , useState,useMemo} from 'react'; import Training from '../organisms/training'; import { useForm } from 'react-hook-form'; import { calculateToday } from '../today/today'; import './trainingList.scss'; type training = { trainingName: string, } const TrainingList: FC = () => { const { register , handleSubmit , reset} = useForm<training>(); const [ trainingList, setList ] = useState<training[]>([]); // const today = useMemo(()=> calculateToday(),[]) const today = calculateToday() const onSubmit = (data: training) => { const {trainingName} = data; if (!trainingName) { alert('トレーニング名を入力してください。') return } else if (trainingList.some((name) => name.trainingName === trainingName )) { alert("そのトレーニングは既に登録されています。") reset() return } setList([ ...trainingList, {trainingName:trainingName} ]); reset(); } return ( <div className='container'> <form className='input-form' onSubmit={handleSubmit(onSubmit)}> <input name='trainingName' ref={register} placeholder='training name'/> <input type='submit'/> </form> <div className='main'> { trainingList.map((list) => { return( <Training trainingName={list.trainingName} key={list.trainingName} today={today} /> ) }) } </div> </div> ) } export default TrainingList;useMemoあり
useMemoを使用すると、calculateToday関数の実行を1回だけにすることができ、下記の検証ツールをご覧いただければ分かる通り、余計な再計算を無くすことができました。
コードは上記コードのTrainingListコンポーネントの上から4行目のコードの代わりに、3行目のコメントアウトしている部分を復活させるだけです。
const today = useMemo(()=> calculateToday(),[]) // const today = calculateToday()最後に
ここまで偉そうに書いてきましたが、業務未経験のため、そもそももっと良い実装方法、UIがあるかもしれませんが、useMemoの活用例の1例として皆様に共有できればと思いました!ご指摘等、厳しい意見お待ちしております!
- 投稿日:2020-11-21T09:37:13+09:00
create-react-app でtypescriptテンプレート指定した場合の対処法
最新のcreate-react-appでtypescriptを指定した場合、下記エラーが発生するため私がとった対応策です
npx create-react-app <プロジェクト名> --template typescript
でプロジェクト作成後 npm start すると下記エラー↓TypeError: Cannot assign to read only property 'jsx' of object ''
*エラー文以下省略対応策
①srcフォルダ内にあるpackage.jsonのreact-scriptsとtypescriptのバージョンを下記内容に書き換える。
"react-scripts": "4.0.0-next.98",
"typescript": "^4.1.0",②VS codeのターミナルで下記コマンド実行
rm -rf node_modules package-lock.json && npm i
③npm start
私はこれで対処できました!
- 投稿日:2020-11-21T09:37:13+09:00
create-react-app typescriptテンプレート指定時エラーの対処法
最新のcreate-react-appでtypescriptを指定した場合、下記エラーが発生するため私がとった対応策です
npx create-react-app <プロジェクト名> --template typescript
でプロジェクト作成後 npm start すると下記エラー↓TypeError: Cannot assign to read only property 'jsx' of object ''
*エラー文以下省略対応策
①srcフォルダ内にあるpackage.jsonのreact-scriptsとtypescriptのバージョンを下記内容に書き換える。
"react-scripts": "4.0.0-next.98",
"typescript": "^4.1.0",②node_modules & package-lock.jsonを削除
③npm install
④npm start
私はこれで対処できました!