Cannot Use @Html.React in Layout Razor View File

1.9k Views Asked by At

It appears that after solving my previous issue with CSS, I've come into another roadblock. I was trying to do the "Optimization: Server-side rendering" exercise from the reactjs.net website. Unfortunately, problem - I can't use the "@Html.React" method in the razor view to pass the Comment Box model. I've tried several what were probably unnecessary imports, and ensuring that my ReactConfig file was updated so that I could use that code. Can anyone tell me what I'm missing here?

ReactConfig.cs:

using React;

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(ReactExamplePractice.ReactConfig), "Configure")]

namespace ReactExamplePractice
{
    using React;
    public static class ReactConfig
    {
        public static void Configure()
        {
            // If you want to use server-side rendering of React components, 
            // add all the necessary JavaScript files here. This includes 
            // your components as well as all of their dependencies.
            // See http://reactjs.net/ for more information. Example:
            //ReactSiteConfiguration.Configuration
            //  .AddScript("~/Scripts/First.jsx")
            //  .AddScript("~/Scripts/Second.jsx");
            ReactSiteConfiguration.Configuration = new ReactSiteConfiguration()
                .AddScript("~/Scripts/react/Tutorial.jsx");
            //ReactSiteConfiguration.Configuration
            //    //.AddScript("~/js/remarkable.min.js")
            //    .AddScript("~/Scripts/react/Tutorial.jsx");
            // If you use an external build too (for example, Babel, Webpack,
            // Browserify or Gulp), you can improve performance by disabling 
            // ReactJS.NET's version of Babel and loading the pre-transpiled 
            // scripts. Example:
            //ReactSiteConfiguration.Configuration
            //  .SetLoadBabel(false)
            //  .AddScriptWithoutTransform("~/Scripts/bundle.server.js")
        }
    }
}

BundleConfig.cs:

using System.Web;
using System.Web.Optimization;
using System.Web.Optimization.React;

namespace ReactExamplePractice
{
    public class BundleConfig
    {
        // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));

            bundles.Add(new BabelBundle("~/bundles/main").Include(
                "~/Scripts/react/Tutorial.jsx"
            ));

            // Forces files to be combined and minified in debug mode
            // Only used here to demonstrate how combination/minification works
            // Normally you would use unminified versions in debug mode.
            BundleTable.EnableOptimizations = true;

            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.validate*"));

            // Use the development version of Modernizr to develop with and learn from. Then, when you're
            // ready for production, use the build tool at https://modernizr.com to pick only the tests you need.
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                        "~/Scripts/modernizr-*"));

            bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                      "~/Scripts/bootstrap.js"));

            bundles.Add(new StyleBundle("~/Content/css").Include(
                      "~/Content/bootstrap.css",
                      "~/Content/site.css"));
        }
    }
}

_Layout.cshtml:

@using System.Web.Optimization.React;
@using React;
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    @*<environment exclude="Development">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
    </environment>*@

    <environment exclude="Development">
        <link href="~/Content/bootstrap.min.css" rel="stylesheet" />
    </environment>

    <title>@ViewData["Title"] - ASP.NET Core Pages Webpack</title>
    @model IEnumerable<ReactExamplePractice.Models.CommentModel>
        @{ 
            Layout = null;
        }
</head>
<body>
    <div class="container">
        <nav class="bg-dark mb-4 navbar navbar-dark navbar-expand-md">
            <a asp-page="/Index" class="navbar-brand">
                <em>ASP.NET Core Pages Webpack</em>
            </a>
            <button aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler" data-target="#topNavbarCollapse" data-toggle="collapse" type="button">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="topNavbarCollapse">
                <ul class="mr-auto navbar-nav">
                    <li class="nav-item">
                        @Html.ActionLink("Application Name", "Index", "Default", new { area = "" }, new { @class = "navbar-brand" })
                    </li>
                    <li class="nav-item">
                        <a asp-page="/About" class="nav-link">About</a>
                    </li>
                    <li class="nav-item">
                        <a asp-page="/Contact" class="nav-link">Contact</a>
                    </li>
                </ul>
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="nav-link" href="https://twitter.com/damien_bod">
                            <img height="30" src="assets/damienbod.jpg" />
                        </a>
                    </li>
                </ul>
            </div>
        </nav>

    </div>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()

        <hr />
        <footer>
            <p>&copy; 2018 - ASP.NET Core Pages Webpack Bootstrap 4</p>
        </footer>
    </div>

    <environment exclude="Development">
        <!-- Optional JavaScript -->
        <!-- jQuery first, then Popper.js, then Bootstrap JS -->
        @*<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>*@
        <script src="~/Scripts/jquery-3.3.1.slim.min.js"></script>
        <script src="~/Scripts/popper.min.js"></script>
        <script src="~/Scripts/bootstrap.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.0/umd/react.development.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.0/umd/react-dom.development.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.0/prop-types.js"></script>
        @Scripts.Render("~/bundles/main");
        @*<script src="@Url.Content("~/Scripts/react/Tutorial.jsx")"></script>*@
    </environment>

    @RenderSection("Scripts", required: false)
</body>
</html>

DefaultController.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using ReactExamplePractice.Models;
using System.Web.Optimization.React;
using React;

namespace ReactExamplePractice.Controllers
{
    public class DefaultController : Controller
    {
        private static readonly IList<CommentModel> _comments;

        static DefaultController()
        {
            _comments = new List<CommentModel>
            {
                new CommentModel
                {
                    Id = 1,
                    Author = "Daniel Lo Nigro",
                    Text = "Hello ReactJS.NET World!"
                },
                new CommentModel
                {
                    Id = 2,
                    Author = "Pete Hunt",
                    Text = "This is one comment"
                },
                new CommentModel
                {
                    Id = 3,
                    Author = "Jordan Walke",
                    Text = "This is *another* comment"
                },
            };
        }

        [OutputCache(Location = OutputCacheLocation.None)]
        public ActionResult Comments()
        {
            return Json(_comments, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public ActionResult AddComment(CommentModel comment)
        {
            //Create a fake ID for this comment or something
            comment.Id = _comments.Count + 1;
            _comments.Add(comment);
            return Content("Success :)");
        }

        // GET: Default
        public ActionResult Index()
        {
            return View();
        }
    }
}

Tutorial.jsx:

class CommentBox extends React.Component {
    constructor(props) {
        super(props);
        this.state = { data: this.props.initialData };
        this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
    }

    loadCommentsFromServer() {
        const xhr = new XMLHttpRequest();
        xhr.open('get', this.props.url, true);
        xhr.onload = () => {
            const data = JSON.parse(xhr.responseText);
            this.setState({ data: data });
        };
        xhr.send();
    }

    handleCommentSubmit(comment) {
        const comments = this.state.data;
        // Optimistically set an id on the new comment. It will be replaced by an
        // use a more robust system for ID generation.
        // id generated by the server. In a production application you would likely
        comment.Id = comments.length + 1;
        const newComments = comments.concat([comment]);
        this.setState({data: newComments});

        const data = new FormData();
        data.append('Author', comment.Author);
        data.append('Text', comment.Text);

        const xhr = new XMLHttpRequest();
        xhr.open('post', this.props.submitUrl, true);
        xhr.onload = () => this.loadCommentsFromServer();
        xhr.send(data);
    }

    componentDidMount() {
        window.setInterval(() => this.loadCommentsFromServer(), this.props.pollInterval);
    }

    render() {
        return (
            <div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm onCommentSubmit={this.handleCommentSubmit} />
            </div>
        );
    }
}

class CommentList extends React.Component {
    render() {
        const commentNodes = this.props.data.map(comment => (
            <Comment author={comment.Author} key={comment.Id}>
                {comment.Text}
            </Comment>
            ));
        return (
            <div className="commentList">
                {commentNodes}
      </div>
        );
    }
}

class CommentForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = { author: '', text: '' };
        this.handleAuthorChange = this.handleAuthorChange.bind(this);
        this.handleTextChange = this.handleTextChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleAuthorChange(e) {
        this.setState({author: e.target.value});
    }

    handleTextChange(e) {
        this.setState({ text: e.target.value });
    }

    handleSubmit(e) {
        e.preventDefault();
        const author = this.state.author.trim();
        const text = this.state.text.trim();
        if (!text || !author) {
            return;
        }
        this.props.onCommentSubmit({Author: author, Text: text});
        this.setState({ author: '', text: '' });
    }

    render() {
        return (
            <form className="commentForm" onSubmit={this.handleSubmit} >
                <input type="text" placeholder="Your name" value={this.state.author} onChange={this.handleAuthorChange} />
                <input type="text" placeholder="Post something here!" value={this.state.text} onChange={this.handleTextChange} />
                <input type="submit" value="Post" />
      </form>
        );
    }
}

class Comment extends React.Component {
    rawMarkup() {
        const md = new (global.Remarkable || window.Remarkable)();
        const rawMarkup = md.render(this.props.children.toString());
        return { __html: rawMarkup };
    }
    render() {
        return (
            <div className="comment">
                <h2 className="commentAuthor">
                    {this.props.author}
                </h2>
                <span dangerouslySetInnerHTML={this.rawMarkup()} />
            </div>
        );
    }
}

class HelloWorld extends React.Component {
    render() {
        return (
            <div>
                <h1> Hello World React JS! </h1>
    </div>)  
                    }  
                }

//ReactDOM.render(
//    <CommentBox url="/comments" submitUrl="/comments/new"  pollInterval={2000} />,
//    document.getElementById('content')
//);

Any help/insight on this would be greatly appreciated. Thanks a ton in advance!

1

There are 1 best solutions below

0
On BEST ANSWER

So, I've come to discover something odd, and maybe it's just my Visual Studio environment or something else, but when using the Html.React library from the NuGet package, it would fail to load the methods at first - but then, as soon as I saved the project, exited, and opened back up, this intellisense glitch disappeared!

I also want to mention quickly that there is a bug in the Remarkable library in the ASP.NET tutorial online. See the following post:

https://github.com/reactjs/React.NET/issues/349

I believe there is a workaround in there for this bug as well, whoever needs it.

This scenario was a strange one. I hope this post helps someone else down the road.