ASP.NET Core の React テンプレート使いにくい問題
概要
C#って良いですよね。ASP.NETって良いですよね。
と常々思っているので、バックエンドにはASP.NETを使いたいのです。
現状、ASP.NET + Reactを使う場合、以下の選択肢があります。
- バックエンドは
ASP.NET Web API
で作成、フロントエンドはCreate React App
などで別に作成。 - ASP.NET CoreのReactテンプレートを使用する。
前者を選んだ場合、バックエンドとフロントエンドで開発サーバーが別々に立つので、
CORSの設定など、ちょっとメンドイです。
後者を選んだ場合、バックエンドもフロントエンドも同一の開発サーバーで動くし、
Startup.cs
に必要なコードが書いてあるので良い感じです。
ただし、React側のコードや設定が若干古い、というかアレな感じなので厳しいのです。
今回は、このReact側の若干アレな部分をなんとかしよう、という話です。
はじめよう
今回はVisual Studio 2019を使ってプロジェクト作成していきます。
- Visual Studio 2019を起動して、画面右側の
新しいプロジェクトの作成
を選択します。 - 検索窓にReactと入れると、2個ヒットするので、
React.js での ASP.NET Core
を選択します。下の方ですね。
- プロジェクト名とかは適当に入れて、作成しましょう。
ターゲットフレームワークは.NET 5
にしてみます。
package.jsonを見てみよう
プロジェクトができたので、ClientApp\package.json
を見てみます。
package.json
{
"name": "dotnet_core_react_typescript_template",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.1.3",
"jquery": "^3.5.1",
"merge": "^1.2.1",
"oidc-client": "^1.9.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-router-bootstrap": "^0.25.0",
"react-router-dom": "^5.1.2",
"react-scripts": "^3.4.4",
"reactstrap": "^8.4.1",
"rimraf": "^2.6.2"
},
"devDependencies": {
"ajv": "^6.9.1",
"cross-env": "^5.2.0",
"typescript": "^3.7.5",
"eslint": "^6.8.0",
"eslint-config-react-app": "^5.2.0",
"eslint-plugin-flowtype": "^4.6.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.18.3",
"nan": "^2.14.1"
},
"eslintConfig": {
"extends": "react-app"
},
"scripts": {
"start": "rimraf ./build && react-scripts start",
"build": "react-scripts build",
"test": "cross-env CI=true react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"lint": "eslint ./src/"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
- 全体的にバージョンが古い。
- Reactなのにjqueryが入っている(bootstrapの関係でしょうが…)
-
TypeScript
入ってるのに(古いし、ファイルは全て*.jsだが)、eslint-plugin-flowtype
?
みたいな感じで、ちょっとアレな感じがします。
目標
ということで、今回の目標をこんな感じで立てました。
- npmモジュールの最新化、整理
- functionコンポーネント化(React 16.0.0はClassコンポーネントのみ対応)
- TypeScript化
やっていきましょう。
npmモジュールの最新化
npm update 一括
とかでググれば良いんですが、以下の記事を参考にしました。ありがたや。
npm-check-updatesをインストールして、
npm install -g npm-check-updates
一括アップデートします。
ncu -u
npm update
package.json
{
"name": "dotnet_core_react_typescript_template",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.6.0",
"jquery": "^3.6.0",
"merge": "^2.1.1",
"oidc-client": "^1.11.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-bootstrap": "^0.25.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"reactstrap": "^8.9.0",
"rimraf": "^3.0.2"
},
"devDependencies": {
"ajv": "^8.0.5",
"cross-env": "^7.0.3",
"typescript": "^4.2.3",
"eslint": "^7.23.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-flowtype": "^5.4.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.23.1",
"nan": "^2.14.2"
},
"eslintConfig": {
"extends": "react-app"
},
"scripts": {
"start": "rimraf ./build && react-scripts start",
"build": "react-scripts build",
"test": "cross-env CI=true react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"lint": "eslint ./src/"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
最新化されましたね。
あとでパッケージの追加もあるので、一旦ここまでにして次に進みます。
functionコンポーネント化
こんな感じで愚直にやりましょう。
import React, { Component } from 'react';
export class Counter extends Component {
static displayName = Counter.name;
constructor(props) {
super(props);
this.state = { currentCount: 0 };
this.incrementCounter = this.incrementCounter.bind(this);
}
incrementCounter() {
this.setState({
currentCount: this.state.currentCount + 1
});
}
render() {
return (
<div>
<h1>Counter</h1>
<p>This is a simple example of a React component.</p>
<p aria-live="polite">Current count: <strong>{this.state.currentCount}</strong></p>
<button className="btn btn-primary" onClick={this.incrementCounter}>Increment</button>
</div>
);
}
}
import React, { useState } from 'react';
export const Counter = () => {
const [currentCount, setCurrentCount] = useState(0);
const incrementCounter = () => {
setCurrentCount(currentCount + 1);
};
return (
<div>
<h1>Counter</h1>
<p>This is a simple example of a React component.</p>
<p aria-live="polite">
Current count: <strong>{currentCount}</strong>
</p>
<button className="btn btn-primary" onClick={incrementCounter}>
Increment
</button>
</div>
);
};
TypeScript化
npmの追加
devDependenciesにTypeScriptがあるのに、@types系がないので、追加していきます。
npm i --save-dev @types/react @types/react-dom @types/react-router-dom
コンポーネントのTypeScript化
これも愚直にやりましょう。
import React, { useState } from 'react';
export const Counter = () => {
const [currentCount, setCurrentCount] = useState(0);
const incrementCounter = () => {
setCurrentCount(currentCount + 1);
};
return (
<div>
<h1>Counter</h1>
<p>This is a simple example of a React component.</p>
<p aria-live="polite">
Current count: <strong>{currentCount}</strong>
</p>
<button className="btn btn-primary" onClick={incrementCounter}>
Increment
</button>
</div>
);
};
import React, { FC, useState } from 'react';
export const Counter: FC = () => {
const [currentCount, setCurrentCount] = useState(0);
const incrementCounter = () => {
setCurrentCount(currentCount + 1);
};
return (
<div>
<h1>Counter</h1>
<p>This is a simple example of a React component.</p>
<p aria-live="polite">
Current count: <strong>{currentCount}</strong>
</p>
<button className="btn btn-primary" onClick={incrementCounter}>
Increment
</button>
</div>
);
};
package.jsonの整理
不要そうなパッケージを消して、最終的にはこんな感じにしました。
jquery
は消しても大丈夫そう?
package.json
{
"name": "dotnet_core_react_typescript_template",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.6.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"reactstrap": "^8.9.0",
"rimraf": "^3.0.2"
},
"devDependencies": {
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"@types/react-router-dom": "^5.1.7",
"cross-env": "^7.0.3",
"eslint": "^7.23.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-react": "^7.23.1",
"typescript": "^4.2.3"
},
"eslintConfig": {
"extends": "react-app"
},
"scripts": {
"start": "rimraf ./build && react-scripts start",
"build": "react-scripts build",
"test": "cross-env CI=true react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"lint": "eslint ./src/"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
感想
まぁまぁ大変でしたが、出来て良かったです。
フロントのコード編集は、Visual Studio 2019よりVisual Studio Codeの方が
やりやすいですねー。
今回作ったテンプレートは、以下にあります。
cloneしてもらえれば、そのまま実行できるはずです。
Discussion
bootstrap 5 以上なら jquery 消しても大丈夫ですね。