How to pass Serverside parameter in ASP.Core to React? - reactjs

I created a React/Typescript project with dotnet new "ASP.NET Core with React.js".
index.cshtml:
<div id="react-app"></div>
#section scripts {
<script src="~/dist/main.js" asp-append-version="true">
</script>
}
boot.tsx(shortened):
function renderApp() {
ReactDOM.render(
<AppContainer>
<BrowserRouter children={ routes } />
</AppContainer>,
document.getElementById('react-app')
);
}
renderApp();
if (module.hot) {
module.hot.accept('./routes', () => {
routes = require<typeof RoutesModule>('./routes').routes;
renderApp();
});
}
How can I pass ASP.Core generated information(the routes from the controllers) to my react/typescript code?

To use server-side rendering in your application, follow the following steps:
1 - Modify App_Start\ReactConfig.cs (for ASP.NET MVC 4 or 5) or Startup.cs (for ASP.NET Core) to reference your components:
namespace MyApp
{
public static class ReactConfig
{
public static void Configure()
{
ReactSiteConfiguration.Configuration = new ReactSiteConfiguration()
.AddScript("~/Scripts/HelloWorld.jsx");
}
}
}
This tells ReactJS.NET to load all the relevant JavaScript files server-side. The JavaScript files of all the components you want to load and all their dependencies should be included here.
2 - In your ASP.NET MVC view, call Html.React to render a component server-side, passing it the name of the component, and any required props.
#Html.React("HelloWorld", new
{
name = "Daniel"
})
3 - Call Html.ReactInitJavaScript at the bottom of the page (just above the ) to render initialisation scripts. Note that this does not load the JavaScript files for your components, it only renders the initialisation code.
<!-- Load all your scripts normally before calling ReactInitJavaScript -->
<!-- Assumes minification/combination is configured as per previous section -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script>
#Scripts.Render("~/bundles/main")
#Html.ReactInitJavaScript()
4 - Hit the page and admire the server-rendered beauty:
<div id="react1">
<div data-reactid=".2aubxk2hwsu" data-react-checksum="-1025167618">
<span data-reactid=".2aubxk2hwsu.0">Hello </span>
<span data-reactid=".2aubxk2hwsu.1">Daniel</span>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script>
<script src="/Scripts/HelloWorld.js"></script>
<script>ReactDOM.render(HelloWorld({"name":"Daniel"}), document.getElementById("react1"));</script>
The server-rendered HTML will automatically be reused by React client-side, meaning your initial render will be super fast.
If you encounter any errors with the JavaScript, you may want to temporarily disable server-side rendering in order to debug your components in your browser. You can do this by calling DisableServerSideRendering() in your ReactJS.NET config.
For a more in-depth example, take a look at the included sample application (React.Samples.Mvc4).
5 - Server-side only rendering
If there is no need to have a React application client side and you just want to use the server side rendering but without the React specific data attributes, call Html.React and pass serverOnly parameter as true.
#Html.React("HelloWorld", new
{
name = "Daniel"
}, serverOnly: true)
And the HTML will look like the one following which is a lot cleaner. In this case there is no need to load the React script or call the Html.ReactInitJavaScript() method.
<div id="react1">
<div>
<span>Hello </span>
<span>Daniel</span>
</div>
</div>

Related

Why Rich-Results doesn't catch generate structured data with javascript

Generate structured data with javascript. But structured data testing tool doesn't catch any data.
Why Rich-Results doesn't catch generate structured data with javascript.
Google Doc Said:
Google can read JSON-LD data when it is dynamically injected into the page's contents, such as by JavaScript code or embedded widgets in your content management system.
from this doc
and how to do reference here.
But Testing tool doesn't catch the data.
Test Page:
Page A, Page B
Page's JSON-LD data is correct, I copy to Testing tool, it can found.
Web page is build by Next.js^10.2.3.
I used server-side render structured-data before.
Encountered page large than 1M, can't load page.
So I changed it to client-side dynamically injected into the page's contents.
import React from 'react';
import Head from 'next/head';
import { map, keys } from 'ramda';
function SeoSchema({ schema }: { schema: Object }) {
// client side render only
if (!process.browser) return null;
return (
<Head>
{/* schema */}
{map((key) => (
schema[key] && (
<script
type="application/ld+json"
key={`schema-${key}`}
// eslint-disable-next-line
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema[key]) }}
/>
)
), keys(schema))}
</Head>
);
}
export default SeoSchema;
What I'm missing?
If there no answer, I will change back to server-side render structured-data.

creating pure web component from react components

i am trying to build web components from react components, it is all working fine, but there are two problems i am trying to solve:
Is there a way to convert such web components to pure web component (using webpack, transpile or some other way), so that react and other dependencies are not bundled?
Is there a way to just include the required portion of dependencies or it is all/none only, and have to use external setting of webpack to use host's version?
thanks
For the first question, there is no direct way to convert React component into a Web Component. You will have to wrap it into a Web Component Class:
export function MyReactComponent() {
return (
<div>
Hello world
</div>
);
}
class MyWebComponent extends HTMLElement {
constructor() {
super();
// Do something more
}
connectedCallback() {
// Create a ShadowDOM
const root = this.attachShadow({ mode: 'open' });
// Create a mount element
const mountPoint = document.createElement('div');
root.appendChild(mountPoint);
// You can directly use shadow root as a mount point
ReactDOM.render(<MyReactComponent />, mountPoint);
}
}
customElements.define('my-web-component', MyWebComponent);
Of course, you can generalize this and create a reusable function as:
function register(MyReactComponent, name) {
const WebComponent = class extends HTMLElement {
constructor() {
super();
// Do something more
}
connectedCallback() {
// Create a ShadowDOM
const root = this.attachShadow({ mode: 'open' });
// Create a mount element
const mountPoint = document.createElement('div');
root.appendChild(mountPoint);
// You can directly use shadow root as a mount point
ReactDOM.render(<MyReactComponent />, mountPoint);
}
}
customElements.define(name, MyWebComponent);
}
register(MyReactComponent, 'my-web-component');
Same register function can be now re-used across all the components that you want to expose as web components. Further, if your component accepts props that should be passed, then this function can be changed to accept third argument as array of string where each value would be registered as a setter for this component using Object.define. Each time a setter is called, you can simply call ReactDOM.render again.
Now for the second question, there are multiple scenarios with what you are trying to do.
If you are bundling application and loading dependencies like React or others using CDN, then Webpack externals is a way to go. Here you will teach Webpack how to replace import or require from the global environment where app will run.
If you are bundling a library which you intend to publish to NPM registry for others to use in their applications, then you must build your project using library target configuration. Read more about authoring libraries here.
Bundling for libraries is slightly trickier as you will have to decide what will be your output format (common.js, ES Modules or UMD or Global or multiple formats). The ideal format is ES Module if you are bundling for browser as it allows better tree shaking. Webpack previously did not support Module format and has recently started supporting it. In general, I recommend Webpack for applications and Rollup.js for libraries.
If you're looking to do this manually, the answer from Harshal Patil is the way to go. I also wanted to point out a library that I helped create, react-to-webcomponent. It simplifies this process and seamlessly supports React 16-18.
React is not a library made to build native web-components.
Writing web-components by hand is not the best option neither as it won't handle conditions, loops, state change, virtual dom and other basic functionalities out of the box.
React, Vue Svelte and other custom libraries certainly have some pros while native web-components have many other advantages like simplicity of ecosystem and being ported by browsers and W3C.
Some libraries that will help you write native web-components in a modern and handy way:
Lego that is alightweight, native and full-featured in a Vue style.
Nativeweb lightweight and raw web-components
ElemX a proof-of-concept that binds native web-component to ElemX functionalities.
If you really wanted to wrap a React component into a native web component, you could do something like:
class MyComponent extends HTMLElement {
constructor() {
this.innerHTML = '<MyReactComponent />'
}
}
customElements.define('my-component', MyComponent)
And use it in your HTML page <my-component />
You can use remount library
/** Main.jsx */
import React from 'react'
import App from './App'
import { define } from 'remount';
define({'react-counter': App},
{
attributes: ['defaultValue'],
shadow:false
})
/** App.jsx */
import React from "react";
import Counter from "./components/Counter.jsx";
function App({defaultValue}) {
return (
<div>
<h1>Counter Component</h1>
<Counter defaultValue={defaultValue}/>
</div>
)
}
export default App;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<react-counter defaultValue="5"></react-counter>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

Unable to Inject 3rd Party Scripts in Gatsby

The following code is required to be injected into the head for Sovrn display ads:
<script type="application/javascript">var googletag=googletag||{};googletag.cmd=googletag.cmd||[];googletag.cmd.push(function(){googletag.pubads().disableInitialLoad()});</script><script type="application/javascript" src="//ap.lijit.com/www/headerauction/headersuite.min.js?configId=XXXX"></script>
It looks for a div with ID="21728840" to display an ad for that div. I'm using Netlify so injecting this script into the head is not an issue, but Netlify does this post-process. Therefore, by the time, the script loads, it is not able to find the ID since the element is not defined, and returns a 403 error. I looked at many suggestions, and tried to use useEffect in the file where the ad is like so:
export const BlogIndexTemplate = ({ posts, title...
...
useEffect(() => {
var googletag = googletag || {}
googletag.cmd = googletag.cmd || []
googletag.cmd.push(function() {
googletag.pubads().disableInitialLoad()
})
const script = document.createElement('script')
script.async = true
script.src =
'//ap.lijit.com/www/headerauction/headersuite.min.js?configId=XXXX'
document.head.appendChild(script)
}, [])
...
{!!posts.length && (
<section className="section">
<div className="container">
<PostSection posts={filteredPosts} />
</div>
</section>
)}
The ad container is located in PostSection like so:
class PostSection extends React.Component {
...
<div className="sidebar-sticky-container">
<div className="ad-skyscraper-container sticky-widget">
<div id="21728840"></div>
I'm not sure what other approach I could try to get the script injected, and manipulate the div with ID 21728840 to display the ad.
After a lot of research, I discovered that using useEffect and most of the suggestions online are unnecessary for Gatsby. To inject a 3rd party script, one needs to create a gatsby-ssr.js file in the root directory. Inside this file, one should have the following for a basic set up:
const React = require('react')
exports.onRenderBody = function({
setHeadComponents,
setPreBodyComponents
}) {
setHeadComponents([
<script
dangerouslySetInnerHTML={{whateveryouneedtoset}}>
])
setPreBodyComponents([
<script
dangerouslySetInnerHTML={{whateveryouneedtoset}}>
])
}
All options for using the gatsby-ssr.js file can be found here: Gatsby SSR

Pass data from `index.html` to react components

I recently got started with web development. And I am stuck with sth that's probably a trivial problem. I am trying to figure out how I can pass data from my dynamically created index.html to my (typescript) react frontend (created via create-react-app).
Suppose we have a flask web server that, when the '/' resource is requested, gathers some initial user data, instantiates a page template with it and returns that page:
# flask webserver
from flask import Flask
from flask import render_template
app = Flask(__name__)
#app.route('/')
def index():
initial_user_data = {"foo":"bar",
"baz":"biz"}
return render_template('index.html', initial_data=initial_user_data)
if __name__ == '__main__':
app.run(debug=True)
For the sake of simplicity initial_user_data stores hard-coded data here. In my actual use case the dictionary gets populated with various user-specific data items that are read from files etc.
Next, let's assume index.html uses the initial_data.
<!DOCTYPE html>
<html lang="en">
<head>
...
<title>React App</title>
</head>
<body>
<script>
initial_data = {{initial_data | tojson}}
console.log(initial_data)
</script>
<div id="root"></div>
...
</body>
</html>
When we now start the webserver and open a browser to navigate to the page when can see the initial_data being logged to the browser's console output. So far, so good.
Now my problem: how can I pass initial_data to my (typescript) react components? Conceptually I want to do sth like this:
// App.tsx
import React from 'react';
const App: React.FC = () => {
// make use of 'initial_data'
const init_data = initial_data;
return (
<div ...
</div>
);
}
But yarn build will give me
Cannot find name 'initial_data'. TS2304
4 |
5 | const App: React.FC = () => {
> 6 | const init_data = initial_data;
| ^
7 | return (
8 | <div className="App">
9 | <header className="App-header">
How can I make initial_data accessible to my react components?
Edit: If this pattern of passing something from the index.html (that gets created on the backend when a clients connects) to my typescript react components is flawed then I'd also accept an answer that points me to the correct pattern in this case.
Something along the lines of (obviously just making sth up, just trying to illustrate what I mean)
Define a typescript data type that stores the user data that can be accessed from all your components
in your main react component use a life-cycle method like 'componendDidMount' to send a request to the backend to fetch the initial_data
When the response comes back store it in 1)
I'd accept an answer that adds shows some sample code for 1) 2) 3)
Many thanks for your help!
When you pass global variables inside a react component, it's always a better way to pass it using the window object.
In this case, you need to pass it as window.initial_data. This informs the linter and react that it's a global variable. As it is not defined inside the file.

How to deploy two react apps to two different paths using firebase hosting

I have two react web apps (both created using create-react-app) and I would like to deploy them to the same domain on different paths. For example:
myapp.firebaseapp.com/auth
myapp.firebaseapp.com/main
How do I accomplish this?
After searching around in the docs on Firebase under the hosting tab I was able to turn up some suggestions. I believe a Cloud Function might be the best solution for you to accomplish what you're saying (dynamic routing based on the URL).
https://firebase.google.com/docs/hosting/functions
From the link:
Create an HTTP function to your Hosting site Open /functions/index.js
in your favorite editor and replace its contents with the following
code. This will create a simple HTTPS function named bigben.
const functions = require('firebase-functions');
exports.bigben = functions.https.onRequest((req, res) => {
const hours = (new Date().getHours() % 12) + 1 // london is UTC + 1hr;
res.status(200).send(`<!doctype html>
<head>
<title>Time</title>
</head>
<body>
${'BONG '.repeat(hours)}
</body>
</html>`);
});
Direct Hosting requests to your function With rewrite rules you can direct requests that match specific patterns to a single
destination. For example, to direct all requests from the page /bigben
on your Hosting site to execute the bigben function, you would open
firebase.json and add the following rewrite configuration under the
hosting section.
{ "hosting": {
"public": "public",
// Add the following rewrites section *within* "hosting" "rewrites": [ {
"source": "/bigben", "function": "bigben"
} ]
} }
So in your case instead of executing this bigben function you can just use a function to render your app by calling your App() function.
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
I think zeit allows you to have up to 3 different deployments running simultaneously but what you're asking (I believe) is how to add different endpoints to your one firebase app.

Resources