3

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 1

4

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/

Sign up to request clarification or add additional context in comments.

4 Comments

Warning: this is now old UseWebpackDevMiddleware is deprecated and this is way more complicated than necessary at this point.
Сould you tell us more about this? I'm trying to find a good solution to this problem.
@dtk77 I would just use webpack by itself to create your bundle as umd modules, then I would manually import them into your cshtml pages. Your default export for each umd module could be a ReactDOM.render(<YourComponent />, root) where root is passed into the default export function
you can also use Visual Studio task explorer to run a webpack build on before build and a bunch of other cool stuff

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.