Skip to main content

Concepts

Reactant only needs to learn a few APIs(ViewModule, injectable, useConnector, action, state, and createApp) for efficient react development.

In addition to the basics of React, Reactant introduces two new concepts, ViewModule and PluginModule.

Service Module

@state used to decorate a module immutable state, @action used to decorate a function to change the module state. Although the decorated state is an immutable state, you can actually update the state in this module using mutations in the method decorated by @action.

@injectable is used to decorate a module that can be injected. While Reactant also supports modules that do not require @injectable decoration in advance, we recommend @injectable decoration for any module that requires dependency injection.

@injectable parameters in TypeScript and JavaScript are completely different, See @injectable for more details.

For Example, Counter project set up with TypeScript.

import { injectable, action, state } from 'reactant';

@injectable()
class Counter {
@state
count = 0;

@action
increase() {
this.count += 1;
}

@action
decrease() {
this.count -= 1;
}

doSomething() {
//
}
}

Reactant provides dependency injection features, see dependency injection.

View Module

ViewModule is a core concept of Reactant. It will be defined the dependencies and logic between non-view modules and UI components. It embodies the separation of attention, where the separation of UI logic and business logic is coalesced.

Dependency injection of service modules using ViewModule and connection injection of state in component.

import { injectable, ViewModule, useConnector } from 'reactant';
// import `Counter`

@injectable()
class AppView extends ViewModule {
constructor(public counter: Counter) {
super();
}

component() {
const count = useConnector(() => this.counter.count);
return (
<button type="button" onClick={() => this.counter.increase()}>
{count}
</button>
);
}
}

The useConnector parameter also supports object maps, e.g. useConnector(() => { count: this.counter.count }), which works in most scenarios. When the state is updated, useConnector will automatically do the shallow comparison for it to determine if the component needs to be updated.

ViewModulecan also use @state and @action, and generally it will use @computed for computing derived data.

See ViewModule for more information.

Bootstrap

Finally, use createApp and bootstrap to run the project. Reactant configures different dependency injection configurations via createApp, which also supports other configurations such as middleware and Reactant plugins. See more advanced guides about Reactant.

import { render } from 'reactant-web';
import { createApp } from 'reactant';
// need to import `AppView`

const app = createApp({
main: AppView,
render,
});

app.bootstrap(document.getElementById('root'));

This is the demo about reactant-native for native apps.

import { createApp } from 'reactant';
import { render } from 'reactant-native';
import { name as appName } from './app.json';
// need to import `AppView`

const app = createApp({
main: AppView,
render,
});

app.bootstrap(appName);

Plugin Module

Plugin Module is based on Redux middleware and React Context, so the Plugin Module can be encapsulated in a simple API that makes middleware and Context incredibly easy to use.

For example, this is a persistence plugin module that requires only simple configuration to achieve persistence.

import {
StorageOptions,
localStorage,
IStorageOptions,
} from 'reactant-storage';

@injectable({
name: 'bar'
})
class HomeView extends ViewModule {
constructor(public storage: Storage) {
super();
this.storage.setStorage(this, {
whitelist: ['test'],
});
}

@state
test = 'test';
}

const app = createApp({
modules: [
{
provide: StorageOptions,
useValue: {
whitelist: [],
storage: localStorage,
loading: <div>loading</div>,
} as IStorageOptions,
},
],
main: HomeView,
render,
devOptions: {
reduxDevTools: true,
},
});

See PluginModule for more information.