ASP.NET React

ASP.NET Core + React – Rewrite my resume page into Facebook Framework

In this post I will show steps to rewrite my resume page (resume.lukaszsadlocha.pl). It was created some time ago in asp.net core + cshtml razor views but now it is time for change its Front-end to React. I will take the page, move it to one huge React component and then split its part to separated component to show how it can be done.

As a first Step I created new asp.net core with react app. In my code folder I run a command in CMD:

dotnet new react -o lukaszsadlocha_onlineresume_react

Then I open it in Visual Studio, build and get nice looking sample application with navigation and 3 examples:

Step 2 – as usual – I added it to github so it is here  github.com/lukaszsadlocha/lukaszsadlocha_onlineresume_react if you want to skip this post and just get a code.

Step 3 – Let’s look how this application is being built. It is worth to remember that it is a still dotnet Core on back-end so let’s look into startup.cs file to see how it is configured. Inside this file we can see that it is still a MVC application with a routing configured pretty much in the same way as other asp.net core application – there is no magic here.

Same situation is in Program.cs. Main method starts WebHost. So what exactly happen when application appeared in by browser under http://localhost:54147 ? As I noticed in Startup.cs HomeController is a default controller and Index is a default action. HomeController->Index action returns just a view and whole React stuff starts inside Index.cshtml

There is one div tag and script section with main.js loaded

@{
    ViewData["Title"] = "Home Page";
}



<div id="react-app">Loading...&amp;lt;/div>



@section scripts {
    &amp;lt;img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22~%2Fdist%2Fmain.js%22%20asp-append-version%3D%22true%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&amp;amp;lt;script&amp;amp;gt;" title="&amp;amp;lt;script&amp;amp;gt;" />
}

So everything connected with React starts in ~/dist/main.js. Once you open it you can see that it is auto-generated file. To be more precisely it is bundle via webpack and its definition is in webpack.config.js. Inside this file you can see that entry point is pointing to ClientApp/boot.txt and inside ClientApp there are files and logic that I will edit to make the page looking as desired.
Let’s open boot.tsx file

import './css/site.css';
import 'bootstrap';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { BrowserRouter } from 'react-router-dom';
import * as RoutesModule from './routes';
let routes = RoutesModule.routes;

function renderApp() {
    // This code starts up the React app when it runs in a browser. It sets up the routing
    // configuration and injects the app into a DOM element.
    const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
    ReactDOM.render(
        <AppContainer>
            <BrowserRouter children={ routes } basename={ baseUrl } />
        </AppContainer>,
        document.getElementById('react-app')
    );
}

renderApp();

// Allow Hot Module Replacement
if (module.hot) {
    module.hot.accept('./routes', () => {
        routes = require<typeof RoutesModule>
('./routes').routes;
        renderApp();
    });
}

It uses extra package for routing and hot reloading. As my resume page is just a static single page I will remove these packages + references and navigation component.
Files that have been removed

ClientApp/components/Layout.tsx
ClientApp/components/NavMenu.tsx
ClientApp/routes.tsx

And modified boot.tsx

import './css/site.css';
import 'bootstrap';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Home } from './components/Home'

function renderApp() {
    const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
    ReactDOM.render(
        <Home />,
        document.getElementById('react-app')
    );
}

renderApp();

So basically now Home is my main and the only one component on the page
Beside boot.tsx also Home.tsx was modified because I do not want to use routing package:

import * as React from 'react';

export class Home extends React.Component{
    public render() {
        return 

<div>
           ...
        </div>


;
    }
}

Ok, now the page looks like:

Step 4 – Now I will move html markup from previous version of my Online Resume and replace return output from Home component.
Important changes that need to be done in converting html

Remove comments
Rename class -> className
All returning html need to be wrapped into one div
Self-closing html tags like br, hr, img need to be closed
Custom tags as t, sm are not allowed. I needed to replace it e.g. with span and adjust css

Once it is done page has the content but styling and images are missing

Step 5 – So how actually should I add images to top react component? Together with dotnet core React sample app there is a definition for url-loader in webpack

module: {
            rules: [
                { test: /\.tsx?$/, include: /ClientApp/, use: 'awesome-typescript-loader?silent=true' },
                { test: /\.css$/, use: isDevBuild ? ['style-loader', 'css-loader'] : ExtractTextPlugin.extract({ use: 'css-loader?minimize' }) },
                { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
            ]
        },

It will take care of the images that are used inside components. If this is a small image then content is provided by base64 or as regular image file in other case.
So I created images folder under ClientApp and copied there one graphic with Sitecore logo. sitecore_big.png. Inside Home.tsx I went into the line where img tag is defined and just replace

<img className="img-responsive" src="~/images/sitecore_big.png" alt="" />

to

<img className="img-responsive" src={require("../images/sitecore_big.png")} alt="" />

and the image appeared:

Important thing to notice here is that you cannot access http://localhost:54147/images/sitecore_big.png – this file is not there. Instead of this, webpack moved the image to OutputPath defined inside webpack.config.js and according to its logic the file is available under – http://localhost:54147/dist/9675e6cf679b022fb9eb5332f6706a3e.png. It also accessible via Visual Studio under wwwroot/dist folder.

Ok, images are there but styles are missing. I took content of main.css file from my previous resume application and replace a content of ClientApp/css/site.css/. I saved it and got the error on the server:

Module not found: Error: Can't resolve '../images/header-bg.jpg' in 'C:\Code\lukaszsadlocha_onlineresume_react\ClientApp\css'

yes, it makes sense – there is a reference to one more image that is missing. I’ve copied header-bg.jpg into image folder and refreshed the browser. Styles appeared – that’s cool. Notice that name of header-bg.jpg if different after css and images were processed by webpack.

Ok, the page looks better now, but all icons and small graphics are missing. That’s because I forgot about fonts-awesome. It is not difficult to add in into the application. Basically font-awesome adds extra styles to simple html tags and adds content on css ::before pseudo-element. So what I need here is a css added to my component. Firstly I need to get font-awesome from npm. I’ve added it to package.json (while I was writing this post the latest stable version was 4.7.0)

    ....
    "file-loader": "0.11.2",
    "font-awesome": "^4.7.0",
    "isomorphic-fetch": "2.2.1",
    "jquery": "3.2.1",
    ...

Once the package is there I needed to bundle it with webpack. Because I have css rules already I didn’t need to bother with ‘style’ part of font-awesome but inside css there are references to many icons/fonts and other files. To make a new rule to handle this files I’ve added couple lines to webpack.config.js

            rules: [
                { test: /\.tsx?$/, include: /ClientApp/, use: 'awesome-typescript-loader?silent=true' },
                { test: /\.css$/, use: isDevBuild ? ['style-loader', 'css-loader'] : ExtractTextPlugin.extract({ use: 'css-loader?minimize' }) },
                { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' },
                { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader" },
                { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader" }
            ]

And last thing is just to import font-awesome css to the page. I’ve added one line in boot.tsx

import './css/site.css';
import 'font-awesome/css/font-awesome.css';
import 'bootstrap';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Home } from './components/Home'

function renderApp() {
    const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
    ReactDOM.render(
        <Home />,
        document.getElementById('react-app')
    );
}

renderApp();

and now there are nice awesome fonts on the page

Step 6 in this step I wanted to focus on front-end design but My Resume page is a single page application mostly static – so splitting it into components is rather an exercise then a huge need. Nevertheless, let’s start with something really easy – Technologies section. There are 3 similar elements where on the left there is an image and on the right a header and some text. I will call it TechnologyBlock and move it to separated file. So I added a file TechnologyBlock.tsx inside components folder and created simple React (function) component:

import * as React from 'react';

export const TechnologyBlock = function () {
    return (
<div>Hello Technology component</div>

)
}

and in Home.tsx I imported it and added next to Technology section to see if it works:

import * as React from 'react';
import { TechnologyBlock } from './TechnologyBlock';

export class Home extends React.Component {
    public render() {
        return 
<div>
            
<div id="section-topbar">
            ....
            
<div className="col-lg-2 col-lg-offset-1">
                
<h5>TECHNOLOGIES</h5>

                <TechnologyBlock />
            </div>

      ....

It is there so I can change a content and pass few props inside component. I used advantage of typescript and instead of passing props I’ve declared type with properties needed inside this component

import * as React from 'react';

type TechnologyBlockProps = {
    image: string,
    header: string,
    text: string
}

export const TechnologyBlock = function ({ image, header, text }: TechnologyBlockProps) {
    return (
        
<div>
            
<div className="col-lg-5 col-lg-offset-3">
                

<img className="img-responsive" src={image} alt="" />

            </div>

            
<div className="col-lg-4">
                

                    {header}
                    {text}
                

            </div>

        </div>

    )
}

and replaced static content of Home.tsx with new components

            
<div className="container desc">
                
<div className="row">
                    
<div className="col-lg-2 col-lg-offset-1">
                        
<h5>TECHNOLOGIES</h5>

                    </div>

                </div>

                
<div className="row">

                    <TechnologyBlock header="SITECORE" image={require("../images/sitecore_big.png")} text="I've been connected with Sitecore... " />
                    <TechnologyBlock header="SHAREPOINT" image={require("../images/sharePoint.jpg")} text="I worked with SharePoint 2007, 2010... " />
                    <TechnologyBlock header="OTHER TECHNOLOGIES" image={require("../images/other.png")} text="As a big enthusiast of new technologi..." />
                </div>

                
                
            </div>

It looks better now.
At this this post is going to its end. Basically there is no point of splitting static page into more and more ‘static’ React components as this is not what react was design for and based on `TechnologyBlock` component I believe that everyone would be able to create new components

 

All the code is available on GitHub:  lukaszsadlocha_onlineresume_react

Leave a Reply

Your email address will not be published. Required fields are marked *