Giter Club home page Giter Club logo

reactant's Introduction

Reactant Logo


Node CI npm version license

Reactant - A framework for building React applications

Motivation

React is a JavaScript library for building user interfaces, but when we want to develop applications based on React, we often have to do a lot of building configuration and many other libraries choices(Picking and learning a React state library and router library, etc.). We also need to consider how our business logic should be abstracted and structured. Everyone who uses React practices their own perception of how React is built, but it doesn't allow us to quickly focus on the business logic itself. As the application business grows in size, we urgently need a framework that can be easily understood and maintained.

And for the structured design of the application's business logic, separation of concern is a good idea. It requires clear boundaries of liability to avoid low maintainability when UI logic and business logic are mixed. We always want to focus on business logic when building applications. It is one of the business core values of an application. We want it to be easy to maintain, and test. Redux remains most popular state library in React. It is fully accord with immutable principles for React. Redux is just a state container, and we're often at a loss for how to really manage those states. We need a framework for scalable, loosely coupled and easily maintainable React applications.


In order to solve these problems, Reactant was created. It's a framework for React.

Features

Q&A

  1. How does it differ from Angular?

It is different everywhere except for dependency injection.

  1. What is the biggest advantage of Reactant?

It can architect a variety of large React projects. Reactant advocates a lightweight UI, separating the concerns of the application and UI to greatly enhance the maintainability of the project.

Usage

npx reactant-cli init my-app
cd my-app
yarn start

Example


Reactant is very easy to get started. You can try Reactant by visiting the online demo.

Here is the counter example:

import React from 'react';
import { ViewModule, createApp, injectable, useConnector, action, state } from 'reactant';
import { render } from 'reactant-web';

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

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

@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>
    );
  }
}

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

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

Packages

  • reactant
  • reactant-cli
  • reactant-di
  • reactant-last-action
  • reactant-model
  • reactant-module
  • reactant-native
  • reactant-redux
  • reactant-router
  • reactant-router-dom
  • reactant-router-native
  • reactant-share
  • reactant-ssr
  • reactant-storage
  • reactant-template
  • reactant-web

Documentation

You can visit reactant.js.org for more documentation.

reactant's People

Contributors

dependabot[bot] avatar richardotvos avatar unadlib avatar zouyoushun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactant's Issues

Proposal: Optimizing @computed based on deps collection of signal concepts

Relevant Package

  • reactant-module

Description

Since the access to a class's state is via a getter in Reactant, its derived state and that of computed can be implemented in a manner similar to the concept of signals for automatic dependency collection.

The usage is modified as follows:

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

-  @computed((that) => [that.count])
+  @computed
  get num() {
    return this.count + 1;
  }
}

Proposal: coworker for reactant-share

Relevant Package

  • reactant-share

Description

As projects become larger, we need a front-end architecture model that can handle multi-process programming. In reactant-share, we can call this a Coworker. Tasks that consume CPU or occupy request limits in the main process can be transferred to the Coworker for execution, and their status is synchronized back to the main process.

We will add interface in packages/reactant-share/src/interfaces.ts.

interface ISharedAppOptions {
  coworker?: {
    /**
     * Whether to be in coworker.
     */
    isCoworker: boolean;
    /**
     * Importing the injected dependency modules.
     */
    modules: ReactModuleOptions[];
    /**
     * Specify a SharedWorker URL
     */
    workerURL?: string;
  };
}

For example,

In coworker.ts

createSharedApp({
  modules: [],
  main: AppView,
  render: () => {
    //
  },
  share: {
    name: 'SharedWorkerApp',
    type: 'SharedWorker',
    coworker: {
      isCoworker: true,
      modules: [],
      workerURL: 'coworker.bundle.js',
    },
  },
});

Support for disabling decorator syntax

Relevant Package

  • reactant-module
  • reactant-di

Description

Many developers still have concerns about TS and JS decorators. In particular, the decorator is a feature of the language that is not stable over the long term, so support for disabling the decorator in reactant-module and reactant-di is open to discussion.

For example,

@injectable()
class Foo {
  @state
  foo = '';

  @action
  update(text: string) {
    this.foo = text;
  }
}

When disabling the decorator, it's equal to:

decorate(
  class Foo {
    constructor() {
       this.foo = '';
       decorate(this, {
         foo: state,
         update: action,
       });
    }

    update(text: string) {
      this.foo = text;
    }
  },
  injectable
)

Proposal: multiprocessing coworker

Relevant Package

  • reactant-share

Description

To better adapt to the needs of multiprocessing, we want "coworker" to quickly support the multiprocessing module. This would allow for more efficient utilization of multiple modules in high-performance multiprocessing and concurrency, combining them into a higher-performing app.

APIs

We can create a proprietary coworker module using createCoworker().

const [CounterWorker, CounterWorkerOptions] = createCoworker('Counter');

createSharedApp({
  modules: [
    {
      provide: 'NewProxyCounter',
      useClass: ProxyCounter,
    },
    CounterWorker,
    {
      provide: CounterWorkerOptions,
      useValue: {
        useModules: ['NewProxyCounter'],
        worker: new Worker(new URL('./counter-coworker.ts', import.meta.url)),
        isCoworker: false
      } as ICoworkerOptions,
    },
  ],
  // more shared app config
});

Refactor the C/S proxy mechanism with the actor model

reactant-share uses a one-to-many client design, which relies on class method proxies that trigger the server's execution methods.

class Counter {
  @state
  count = 0;

  @action
  _increase() {
    this.count += 1;
  }
  
  @proxy
  async increase() {
    this._increase();
  }
  
  async doSomething() {
    await this.increase();
  }
}

For developers, the principle of using @proxy is difficult to grasp, and the decorated methods often have to be newly written in order to have to be asynchronous, thus necessitating a new simple asynchronous method, which is not worth it.

Therefore, reactant-share requires a more flexible and simple C/S proxy model, and the actor model is well suited for such a design.

class Counter {
  @state
  count = 0;

  @action
  increase() {
    this.count += 1;
  }
  
  async doSomething() {
     await spawn(this, 'increase', []);
  }
}

Subscribe method from another module

Description

I try to subscribe a method from another module but it's not reached

Minimal Reproduction

// layout.view.tsx
@injectable()
export class LayoutView extends ViewModule {
    @state
    drawerOpen: boolean = false
    @action
    toggleDrawer(){ this.drawerOpen = !this.drawerOpen; }
   ...
// parametres.service.ts
@injectable()
export class ParametresService {
    constructor(
        private readonly layout: LayoutView
    ){
        subscribe(this.layout, () => { console.log("this.layout.subscribe"); })
    }
   ...

Expected Behavior

I expect that the method was called.

if i do this it work

setTimeout(() => {
   subscribe(this.layout, () => { console.log("this.layout.subscribe"); })
},1)

But it's not very elegant.

Thanks

Add clientIds for reactant-share

Relevant Package

  • reactant-share

Description

In order to better control multiple clients through the server port, we would like to add clientIds to the PortDetector module of the server port.

Proposal: isolated state for reactant-share

Relevant Package

  • reactant-last-action
  • reactant-share

Description

Design a module identity api that can be unsynchronized, and the state in the instance marked by it will not be synchronized

If the synchronized and unsynchronized state is modified in a Redux action, reactant-last-action sequence counter will count normally and the synchronized state will be sent via reactant-share.

Documentation missing how to create a new instance of a class for each injection

Reactant has been working great for our dependency injection and viewmodules. One thing we haven't figured out from Reactant's documentation, all classes/objects injected are effectively singletons because if 2 classes need the class Foo{}, they get the same instance of it if it already exists in the container. With the constructor injection, how can it request a new object instead of a pre-existing one thats already in the container? I'd prefer not to do useFactory in the modules as the class being injected does have a dependency of its own that is injected.

This might be good to add to the documentation.

For example, how can the 2 classes below, MainView and OtherView get their own instance of Foo?

@injectable({name:"foo"})
class Foo{
....
}

class MainView extends ViewModule {
   constructor(protected foo: Foo){   }
   component(....)
} 

class OtherView extends ViewModule {
   constructor(protected foo: Foo)  {   }
   component(....)
} 

I'd prefer to avoid:
modules: [
  { 
     provide: "foo",
     useFactory: ()=> {return new Foo(...);}
  }
]

As our Foo does have a dependency that Reactant is injecting

Documentation missing on runtime object creation from container

Hi,

Assuming a typical createSharedApp() app, is there a way at runtime for a class to get (or create a new object) from the "main container" for the app? Not sure if the following code is helpful, but typically if I do new Foo() on an object that uses @state/@action, I get errors even if I tried using @autoBind:

Uncaught Error: 'this' in method 'AddFoos' of class 'FooList' decorated by '@action' must be bound to the current class instance.

I am guessing I need to get the object from the container and not do New on it so the @action function is correctly bound to the class. Here is a simple example, although I understand the code could be changed to return a simple Array or Map instead of a FooList but trying to come up with a simple example of wanting to do New myClassName() at runtime instead of constructor injection; This could be good to also add to the documentation.

class Foo{
  @state
  Name: string = "";

  @state
   classifier: string = "";
}

class FooList
{
  @state
  Foos: Array<Foo> = [];
  
  @action
  UpdateFoos(foos: Array<Foo>){.....}
}

class FooView{
   constructor(inject("AllFoos") allFooList: FooList) {}

  get SpecialFoo(): FooList
  {
      const foolist = new FooList(); //This line would be replaced with getting a new FooList from the container...
     foolist.UpdateFoos(this.allFooList.filter((foo)=>......);
     return foolist;
  } 
}

Thank you

Resolve a module not in constructor

First of all, congratulations on this library, i like it ;)

Is it possible, in Module, to resolve an another module but not in constructor ?

Description

I want to make a generic plugin for Authentication with options.

export const AuthOptions = Symbol("AuthOptions");
export interface IAuthOptions {
    path: string
    loginView: Module<ViewModule>
}
const AuhtOptionsDefault: IAuthOptions = {
    path: "/login",
    loginView: LayoutView          /**  <== this is default ViewModule */
}
@injectable()
export class AuthModule extends PluginModule {
    readonly [storeKey]?: any

    authOptions: IAuthOptions;
    constructor(
        @optional(AuthOptions) options: IAuthOptions
    ){
        super();
        this.authOptions = {
            ...AuhtOptionsDefault,
            ...options
        }
    }
    provider = (props: PropsWithChildren<any>) => {
        const componentView = resolve(this.authOptions.loginView);  /** <== how resolve this ? */
        return (
            <>
                <Switch>
                    <Route path={this.authOptions.path} component={componentView.component} />
                </Switch>
                {props.children}
            </>
        )
    }
}

I want to make the component this.authOptions.loginView configurable by the users.
How can i make it ?
thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.