I am trying to add react components (typescript) in the form of .tsx files to be rendered on some of my asp.net core mvc .cshtml pages but cannot seem to get things working properly. Is there a good way to do this?
1 Answer
You basically need to use webpack and modify your .csproj to do a yarn build before running to transpile your typescript and connect all of the dependencies into a bundle.
Start by adding Nuget packages: Yarn.MSBuild and Microsoft.TypeScript.MSBuild to your project.
tsconfig.json: (put in root dir of your project)
{
"typeAcquisition": {
"enable": true
},
"compileOnSave": false,
"compilerOptions": {
"sourceMap": true,
"module": "commonjs",
"target": "es6",
"noImplicitAny": false,
"jsx": "react",
"outDir": "wwwroot/dist",
"moduleResolution": "node"
},
"exclude": [
"node_modules",
"wwwroot"
]
}
^^Note that this will make it so that your .tsx files transpile into folder: wwwroot/dist, change outDir if you want it somewhere else
package.json (put in root dir of your project)
{
"scripts": {
"webpack": "webpack"
},
"devDependencies": {
"@types/react": "^16.4.6",
"@types/react-dom": "^16.0.6",
"@types/webpack-env": "^1.13.6",
"aspnet-webpack": "^3.0.0",
"awesome-typescript-loader": "^5.2.0",
"babel-core": "^6.26.3",
"bootstrap": "4.3.1",
"clean-webpack-plugin": "^0.1.19",
"typescript": "^2.9.2",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.22.2"
},
"dependencies": {
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-hot-loader": "^4.0.0"
}
}
^^add whatever npm dependencies you need in your package.json (obviously) as well as your typescript @type dependencies. These are just the minimum viable packages for react and hot reloading.
Webpack.targets (put in root dir of your project)
<Project>
<Target Name="EnsureNodeModulesInstalled"
BeforeTargets="Build"
Inputs="yarn.lock;package.json"
Outputs="node_modules/.yarn-integrity">
<Yarn Command="install" />
</Target>
<Target Name="PublishWebpack"
BeforeTargets="Publish">
<ConvertToAbsolutePath Paths="$(PublishDir)">
<Output TaskParameter="AbsolutePaths" PropertyName="AbsPublishDir" />
</ConvertToAbsolutePath>
<Yarn Command="run webpack --env.prod --env.publishDir=$(AbsPublishDir)" />
</Target>
</Project>
^^this file Webpack.target is to be added to your .csproj file (I added it at the end) and should be added like so:
In your .csproj:
<Import Project="Webpack.targets" />
webpack.config.js: (put in root dir of your project)
const path = require('path');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
const outputDir = (env && env.publishDir)
? env.publishDir
: __dirname;
return [{
mode: isDevBuild ? 'development' : 'production',
devtool: 'inline-source-map',
stats: { modules: false },
entry: {
'App': './ClientApp/App.tsx',
},
watchOptions: {
ignored: /node_modules/
},
output: {
filename: "dist/[name].js",
path: path.join(outputDir, 'wwwroot'),
publicPath: '/'
},
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".json"]
},
devServer: {
hot: true
},
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{
test: /\.tsx?$/,
include: /ClientApp/,
loader: [
{
loader: 'awesome-typescript-loader',
options: {
useCache: true,
useBabel: true,
babelOptions: {
babelrc: false,
plugins: ['react-hot-loader/babel'],
}
}
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(path.join(outputDir, 'wwwroot', 'dist')),
new CheckerPlugin()
]
}];
};
^^Note the entry point and modules->rules->include. That means webpack is being configured to look in a folder called ClientApp (in the root dir of your project) for the .tsx files to be transpiled. If you want your .tsx files elsewhere simply change these up. The entry point also specifies that the file entry point is App.tsx, put your highest level react component (that depends on all of your other components) into here or simply include it in this file.
Now add WebpackDevMiddleware to your Startup->Configure function:
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
Now if in your App.tsx file you select a div (say one with id: 'root') and render your component in it, all you need to do to add your React component to your .cshtml file is include the transpiled App.js (in wwwroot/dist) to the .cshtml along with a div with id: 'root' and bam. You have react components that can be added to .cshtml without using any SPA bs 🚀
Credit: https://natemcmaster.com/blog/2018/07/05/aspnetcore-hmr/