Directlink
Library for building single-page web applications
Install / Use
/learn @directlinkcore/DirectlinkREADME
DirectLink
Library for building single-page web applications that makes the world easier for ASP.NET Core developers who use React.
Features
- Server-side rendering
- Real-time communications:
- invoke .NET methods from React components
- set state of components from .NET world
- Flexible server-side routing
Installation
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
<details>
<summary><code>package.json</code></summary>
{
"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"
}
}
</details>
<details>
<summary>add <code>webpack.config.js</code></summary>
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']
}
}
}]
}
};
</details>
<br>
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"));
<details>
<summary><code>Startup.cs</code></summary>
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"));
}
}
}
</details>
Usage
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.
Documentation
See live samples and documentation for more details.
License
Apache 2.0
Related Skills
node-connect
343.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
90.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
