Framework for creation of the React apps using Express and Socket.io. Sample app can be cloned from GitHub.
npm install reacting-squirrel --save
// ./index.js
import Server from 'reacting-squirrel/server';
const app = new Server();
app.registerRoute('get', '/', 'home', 'Home');
app.start();
// ./app/home.js
import React from 'react';
import { Page } from 'reacting-squirrel';
export default class HomePage extends Page {
render() {
return (
<div>
<h1>Home</h1>
</div>
);
}
}
This code will start simple app on the default port with one page which will be the home page.
// ./index.js
import Server from 'reacting-squirrel/server';
import UserSocket from './socket.user';
import UserStore from './user-store';
const app = new Server({
auth: (session, next) => {
UserStore.load(session.id, (err, user) => {
if (err) {
next(err);
return;
}
session.setUser(user);
next();
});
}
});
app.registerRoute('get', '/', 'home', 'Home');
app.registerSocketClass(UserSocket);
app.start();
// ./socket.user.js
import { SocketClass } from 'reacting-squirrel/server';
export default class User extends SocketClass {
load(socket, data, next) {
next(null, this.getUser());
}
}
// ./app/home.js
import React from 'react';
import { Page } from 'reacting-squirrel';
export default class HomePage extends Page {
state = {
user: null,
};
componentDidMount() {
super.componentDidMount();
this.request('user.load', (err, user) => {
if (err) {
alert(err.message);
return;
}
this.setState({ user });
});
}
render() {
const { user } = this.state;
return (
<div>
<h1>Home</h1>
<h2>{user ? user.name : 'Loading...'}</h2>
</div>
);
}
}
This code will start simple app on the default port. After the page load the user.load
event is emitted and UserSocket
class is trying to load the logged user and send it back to the page.
The simple server can be started over the CLI using ./node_modules/.bin/rs-start-server
and creating rsconfig.json
in application root.
All components rendered by the application can be wrapped with Provider
such as Context.Provider
or ThemeProvider
. Only thing needed is to register the provider with server method, rsconfig or plugin.
The routes are registered on the server-side. The module is using express based routes registering.
import Server from 'reacting-squirrel/server';
const app = new Server();
// On the route '/' will be rendered the content component located in {config.appDir}/home with 'Home' title.
app.registerRoute('get', '/', 'home', 'Home');
app.start();
The socket events can be directly registered. It should be used for simple socket events which don't need authorization.
import Server from 'reacting-squirrel/server';
const app = new Server();
// Frontend app can emit 'test' with some data. The event's listener emits the data back.
app.registerSocketEvent('test', (socket, data, next) => next(null, data));
app.start();
The socket classes can handle multiple socket events prefixed by the class name. After the registration of the socket class socket events are automatically registered to the server app.
// ./socket.user.js
import { SocketClass } from 'reacting-squirrel/server';
export default class User extends SocketClass {
load(socket, data, next) {
// sends the authorized user data after the 'user.load' socket request
next(null, this.getUser());
}
async updateUser(socket, data) {
await doSomeAsyncOperation();
return this.getUser();
}
}
// ./index.js
import Server from 'reacting-squirrel/server';
import UserSocket from './socket.user';
import UserStore from './user-store';
const app = new Server({
auth: (session, next) => {
UserStore.load(session.id, (err, user) => {
if (err) {
next(err);
return;
}
session.setUser(user);
next();
});
}
});
// Registeres the socket class
app.registerSocketClass(UserSocket);
app.start();
The module can register custom components which are rendered in custom DOM element in the layout.
import Server from 'reacting-squirrel/server';
const app = new Server();
// Frontend app tries to render component located at {config.appDir}/test to the DOM element with id 'test'
app.registerComponent('test', 'test');
app.start();
RSConfig file can contain list of routes, list of components and the directory with the socket classes. By default the file rsconfig.json
is searched in the process.cwd()
directory. If the file doesn't exist nothing happens. The path to the file can by changed with config.rsConfig option.
The schema for the file is located in [pathToModule]/schemas/rsconfig.schema.json
.
// rsconfig.json
{
"routes": [
{
"route": "/",
"component": "home",
"title": "Home",
"requiredAuth": false,
"method": "GET"
}
],
"components": [
{
"id": "test",
"component": "test"
}
],
"socketClassDir": "./dist/network/socket",
"errorPage": "error-page"
}
App config can be also be defined in the rsconfig.
Values in rsconfig that have $env:[value]|[defaultValue]
prefix are replaced with process.env[value]
in the server start.
If the env var is not in the process the default value is used (if specified).
In the startup process, the res
directory is created in app
directory. In that directory is created default text file text.json
. The content of the text file is used as default dictionary using texting-squirrel module.
All components have getText
method to access the text from the dictionary.
import { Component } from 'reacting-squirrel';
export default class CustomComponent extends Component {
render() {
return <h1>{this.getText('title')}</h1>;
}
}
The module contains a text component to handle dictionary directly from the JSX code.
import { Component, Text } from 'reacting-squirrel';
export default class CustomComponent extends Component {
render() {
return <Text dictionaryKey="title" tag="h1" />
}
}
The instance of the server has a property Text
which is just texting-squirrel
module with registered directories.
Server option locale
defines supported locales and dictionary text files are created (if not exist) and registered in the server startup.
If the page title starts with the :
the key (without the first character) is searched in the locale dictionary.
The dictionaries can be registered for different languages. The accepted languages should be set with the locale.accepted
option on the server and dictionaries will be created in the res
directory. The default language is taken from the browser (the dictionary must exist).
The locale can be changed on the client side using Application.setLocale
method and it's handled on the server side (still experimental) with cookie rs~locale
.
NOTE: In case of setting locale to the default dictionary it has to be the default
keyword instead of the value.
The module is using socket.io as a default communication protocol. The payload is chunked (default 10kB per chunk) and sent to the server.
File upload can be diffucult over websocket. Without chunks big files disconnects the socket because of the pingTimeout
. The file is sent to the server in chunks and converted to buffer on the server side.
const file = 'get somehow a File instance';
Socket.emit('file.upload', undefined, { file }, (progress) => console.log(progress));
The server limits the message size. If the size is bigger than allowed limit, the socket is disconnected. The module has 100MB cap for the message size.
Decorators are designed for the the SocketClass
methods.
Data returned in the method are broadcasted to the client side.
Before the method execution is checked the logged user in the session. If it's not the error is thrown.
The method is not registered as socket method and cannot be called from the client side.
The response is casted to defined types using runtime-type module.
The plugins can be registered with Server.registerPlugin
method. The plugin should extent Plugin
class in the module.
Plugin can:
getPages
method. The path to the component can be absolute so it must not be in the app
directory.
getComponents
method. The path to the component can be also absolute. Checkout the documentation here.
Generated using TypeDoc