Library for building single-page web applications that makes the world easier for ASP.NET Core developers who use React.
- Server-side rendering
- Real-time communications:
- invoke .NET methods from React components
- set state of components from .NET world
- Flexible server-side routing
Create ASP.NET Core
project and add package:
dotnet new web -o Sample && cd Sample
dotnet remove package Microsoft.AspNetCore.App
dotnet add package Microsoft.AspNetCore.App -v 2.1.5
dotnet add package DirectLink.Core.React
Initialize npm and install packages:
npm init -y
npm install directlink-react
npm install -D @babel/core babel-loader webpack webpack-cli
npm install -D @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties
package.json
{
"name": "sample",
"version": "1.0.0",
"description": "",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"directlink-react": "1.0.5"
},
"devDependencies": {
"@babel/core": "7.1.2",
"@babel/plugin-proposal-class-properties": "7.1.0",
"@babel/preset-env": "7.1.0",
"@babel/preset-react": "7.0.0",
"babel-loader": "8.0.4",
"webpack": "4.23.1",
"webpack-cli": "3.1.2"
}
}
add webpack.config.js
let path = require('path');
module.exports = {
mode: 'development',
entry: { app: 'app.jsx' },
output: {
path: path.join(__dirname, 'wwwroot/dist'),
filename: '[name].js'
},
resolve: {
modules: ['clientApp', 'node_modules']
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: ['@babel/plugin-proposal-class-properties']
}
}
}]
}
};
Add service and configure pipeline in Startup.cs
:
services.AddDirectLink<App>(tags => tags.AddDefaultTemplateTags(title: "Sample"));
app.UseDirectLink(components => components.Map<App>(script: "/dist/app.js"));
Startup.cs
using DirectLinkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Sample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDirectLink<App>(tags => tags.AddDefaultTemplateTags(title: "Sample"));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseDirectLink(components => components.Map<App>(script: "/dist/app.js"));
}
}
}
Add app.jsx
to clientApp
folder:
components.App = class App extends React.Component {
constructor(props) {
super(props);
directlink.init(this);
}
componentWillUnmount() {
directlink.dispose(this);
}
sendMessage = () => {
this.AddMessage(this.state.message) //here we invoke App.AddMessage method
.then(() => this.setState({ message: '' }));
}
onChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
}
onKeyPress = (event) => {
if (event.key === 'Enter') {
this.sendMessage();
}
}
render() {
return (
<div className="jumbotron">
<div className="row mb-3">
<div className="col-12 col-sm-8 col-md-9 col-lg-10 mb-3 mb-sm-0">
<input type="text" className="form-control" name="message"
placeholder="message" autoComplete="off"
value={this.state.message} onChange={this.onChange} onKeyPress={this.onKeyPress} />
</div>
<div className="col-12 col-sm-4 col-md-3 col-lg-2">
<button type="button" className="btn btn-primary w-100"
onClick={this.sendMessage}>Send</button>
</div>
</div>
<ul className="list-unstyled">
{this.state.Messages.slice().reverse().map(message =>
<li key={message.Id}>
<div className='alert alert-primary'>{message.Text}</div>
</li>)}
</ul>
</div>
);
}
};
Add Message.cs
:
using System;
namespace Sample
{
public class Message
{
public Guid Id { get; }
public string Text { get; }
public Message(string text) => (Id, Text) = (Guid.NewGuid(), text);
}
}
Add App.cs
:
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using DirectLinkCore;
namespace Sample
{
public class App : DirectLinkDispatcher<AppViewModel>
{
private static ConcurrentQueue<Message> _messages = new ConcurrentQueue<Message>();
public IReadOnlyCollection<Message> GetMessages() => _messages;
public async Task AddMessage(string text)
{
_messages.Enqueue(new Message(text));
await SetStateAsync(new { Messages = _messages }); //here we setState of App component
}
}
}
Add AppViewModel.cs
:
using System.Collections.Generic;
using DirectLinkCore;
namespace Sample
{
public class AppViewModel : ViewModel
{
public IReadOnlyCollection<Message> Messages { get; }
public AppViewModel(App app) => Messages = app.GetMessages();
}
}
Build client and run project:
npm run build
dotnet run
Open http://localhost:5000 in several tabs and check that all clients can text each other.
See live samples and documentation for more details.
Apache 2.0