Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mjolnirjs/stated-bean
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.8.1
Choose a base ref
...
head repository: mjolnirjs/stated-bean
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.8.2
Choose a head ref
  • 6 commits
  • 23 files changed
  • 2 contributors

Commits on Oct 15, 2019

  1. Copy the full SHA
    81847a1 View commit details
  2. chore: 🤖 release 0.8.2 (#94)

    fix: 🐛 bean object is not correctly gc after component unmount
    foreleven authored Oct 15, 2019
    Copy the full SHA
    f4dc51d View commit details

Commits on Oct 16, 2019

  1. docs: ✏️ update README

    foreleven committed Oct 16, 2019
    Copy the full SHA
    11affff View commit details
  2. docs: ✏️ update README

    foreleven committed Oct 16, 2019
    Copy the full SHA
    6188159 View commit details
  3. docs: ✏️ examples by stackblitz (#95)

    * docs: ✏️ examples by stackblitz
    foreleven authored Oct 16, 2019
    Copy the full SHA
    ca34635 View commit details
  4. chore(release): 0.8.2

    JounQin committed Oct 16, 2019
    Copy the full SHA
    5d9811d View commit details
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -3,3 +3,4 @@ coverage
dist
lib
CHANGELOG.md
.history
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ before_install:
install: yarn --frozen-lockfile

before_script:
- export PARSER_NO_WATCH=true
- export TRAVIS_REPO_OWNER=${TRAVIS_REPO_SLUG%/*}
- export TRAVIS_REPO_NAME=${TRAVIS_REPO_SLUG#*/}

7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [0.8.2](https://github.com/mjolnirjs/stated-bean/compare/v0.8.1...v0.8.2) (2019-10-16)


### Bug Fixes

* 🐛 bean object is not correctly gc after component unmount ([81847a1](https://github.com/mjolnirjs/stated-bean/commit/81847a17c4c83960c021688433e7447f47af61f7))

### [0.8.1](https://github.com/mjolnirjs/stated-bean/compare/v0.8.0...v0.8.1) (2019-10-15)

## [0.8.0](https://github.com/mjolnirjs/stated-bean/compare/v0.7.0...v0.8.0) (2019-10-13)
228 changes: 167 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -14,25 +14,32 @@
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![codechecks.io](https://raw.githubusercontent.com/codechecks/docs/master/images/badges/badge-default.svg?sanitize=true)](https://codechecks.io)

> A light but scalable state management library with react hooks, inspired by [unstated-next](https://github.com/jamiebuilds/unstated-next).
> It allows you to manage the state data of multiple views together. Make cross-component data transfer simple.
> A light but scalable `view-model` library with react hooks.
## TOC <!-- omit in TOC -->

- [Features](#features)
- [Online Demo](#online-demo)
- [Usage](#usage)
- [Define a StatedBean](#define-a-statedbean)
- [Plain object StatedBean](#plain-object-statedbean)
- [Class StatedBean](#class-statedbean)
- [Singleton and Named StatedBean](#singleton-and-named-statedbean)
- [Define a named bean](#define-a-named-bean)
- [Declare as a named bean when `useBean`](#declare-as-a-named-bean-when-usebean)
- [Provider container and inject the singleton bean](#provider-container-and-inject-the-singleton-bean)
- [Auto inject and watch the props](#auto-inject-and-watch-the-props)
- [Effect action state and observer](#effect-action-state-and-observer)
- [API](#api)
- [Decorators](#decorators)
- [`StatedBean`](#statedbean)
- [`Stated`](#stated)
- [`PostProvided`](#postprovided)
- [`AfterProvided`](#afterprovided)
- [`Effect`](#effect)
- [`Props` and `ObservableProps`](#props-and-observableprops)
- [use Hooks](#use-hooks)
- [`useBean`](#usebean)
- [`useInject`](#useinject)
- [Get the instance from the container in the `React Context`](#get-the-instance-from-the-container-in-the-react-context)
- [Create the temporary instance for current `Component`](#create-the-temporary-instance-for-current-component)
- [`UseStatedBeanOption`](#usestatedbeanoption)
- [`useObserveEffect`](#useobserveeffect)
- [Provider](#provider)
@@ -60,13 +67,53 @@ npm i stated-bean

## Usage

### Define a StatedBean

#### Plain object StatedBean

```tsx
import { useBean } from 'stated-bean';

const CounterModel = {
count: 0,
decrement() {
this.count--;
},
increment() {
this.count++;
},
};

function CounterDisplay() {
const counter = useBean(() => CounterModel);

return (
<div>
<button onClick={counter.decrement}>-</button>
<span>{counter.count}</span>
<button onClick={counter.increment}>+</button>
</div>
);
}

function App() {
return (
<StatedBeanProvider>
<CounterDisplay />
</StatedBeanProvider>
);
}
```

#### Class StatedBean

```ts
import { StatedBean, Stated, useBean } from 'stated-bean';
import { StatedBean, Stated useBean } from 'stated-bean';

@StatedBean()
export class Counter {
class CounterModel {
@Stated()
count: number = 0;
count = 0;

increment() {
this.count++;
@@ -78,14 +125,110 @@ export class Counter {
}

function CounterDisplay() {
const counter = useBean(Counter);
const counter = useBean(CounterModel);

return (
<div>
<button onClick={counter.decrement}>-</button>
<span>{counter.count}</span>
<button onClick={counter.increment}>+</button>
</div>
// ...
);
}
```

### Singleton and Named StatedBean

The named bean singleton bean can be resolved via `useInject` with the special name.

#### Define a named bean

```ts
@StatedBean('SpecialName')
class NamedBean {}

@StatedBean({ singleton: true })
class SingletonBean {}
```

#### Declare as a named bean by `useBean`

```ts
const model = useBean(CounterModel, { name: 'SpecialName' });
```

### Provider container and inject the singleton bean

The beans was stored in the `StatedBeanContainer` witch be created by the `StatedBeanProvider` and bind to the React context.
`useInject` will find the named bean from the container or it's parent container.

```tsx
@StatedBean({ singleton: true })
class UserModel {
@Stated()
user = { name: 'jack' };
}

function App() {
return (
<StatedBeanProvider providers={[UserModel]}>
<UserDisplay />
</StatedBeanProvider>
);
}

function UserDisplay() {
const model = useInject(UserModel);

return model.user.name;
}
```

### Auto inject and watch the props

```tsx
@StatedBean()
class InputModel implements InitializingBean {
@Props('initialValue')
@Stated()
value: number;

@ObservableProps()
value$: BehaviorSubject<number>;

afterProvided() {
this.value$.subscribe(v => {
this.value = v;
});
}
}

function Input(props: InputProps) {
const model = useBean(InputModel, { props });

return (
// input component
);
}
```

### Effect action state and observer

```tsx
@StatedBean()
class SearchModel {

@Effect()
search() {
return fetchUsers();
}
}

const UserTable() {
const model = useBean(SearchModel);
const { loading, error } = useObserveEffect(model, "search");

if (loading) {
return <Loading />;
}
return (
// ...user table
);
}
```
@@ -106,18 +249,25 @@ _Signature_: `@Stated(): PropertyDecorator`

Indicates that an annotated property is `Stated`. Its reassignment will be observed and notified to the container.

#### `PostProvided`
#### `AfterProvided`

_Signature_: `@PostProvided(): MethodDecorator`
_Signature_: `@AfterProvided(): MethodDecorator`

The `PostProvided` decorator is used on a method that needs to be executed after the `StatedBean` be instanced to perform any initialization.
The `AfterProvided` decorator is used on a method that needs to be executed after the `StatedBean` be instanced to perform any initialization.

#### `Effect`

_Signature_: `@Effect(name?: string | symbol): MethodDecorator`

The `Effect` decorator is used on a method that can get the execution state by `useObserveEffect`.

#### `Props` and `ObservableProps`

_Signature_: `@Props(name?: string): PropertyDecorator` `@ObservableProps(name?: string): PropertyDecorator`

The `Props` decorator is used on a property that can sync the value from props.
The `ObservableProps` decorator is used on a `BehaviorSubject` property. You can subscribe the next new props value.

### use Hooks

#### `useBean`
@@ -132,34 +282,6 @@ _Signature_: `useInject<T>(type: ClassType<T>, option: UseStatedBeanOption<T> =

The `useInject` will get the instance of the stated bean from the `StatedBeanContainer` in the context and listen for its data changes to trigger the re-rendering of the current component.

##### Get the instance from the container in the `React Context`

```tsx
function SampleComponent() {
const model = useInject(UserModel);
// ...
}

function App() {
return (
<StatedBeanProvider types={[UserModel]}>
<SampleComponent />
</StatedBeanProvider>
);
}
```

##### Create the temporary instance for current `Component`

```tsx
function SampleComponent() {
const model = useBean(() => new UserModel());

// pass the model to its children
return <ChildComponent model={model} />;
}
```

##### `UseStatedBeanOption`

```ts
@@ -175,22 +297,6 @@ _Signature_: `useObserveEffect(bean: StatedBeanType, name: string | symbol): Eff

observe the execution state of the method which with `@Effect`.

```ts
@StatedBean
class UserModel {
@Effect()
fetchUser() {
// ...
}
}

const UserInfo = () => {
const model = useBean(() => new UserModel());
const { loading, error } = useObserveEffect(model, 'fetchUser');
// ...
};
```

### Provider

#### `<StatedBeanProvider {...props: StatedBeanProviderProps} />`
30 changes: 2 additions & 28 deletions example/index.tsx
Original file line number Diff line number Diff line change
@@ -2,9 +2,7 @@ import 'reflect-metadata';

import { ReflectiveInjector } from 'injection-js';

import { Counter } from './src/components/Counter';
import { TodoApp } from './src/components/Todo';
import { TodoModel } from './src/models/TodoModel';
import { App } from './src/App';
import { TodoService } from './src/services/TodoService';

import React from 'react';
@@ -13,7 +11,6 @@ import {
BeanDefinition,
IBeanFactory,
StatedBeanApplication,
StatedBeanProvider,
} from 'stated-bean';

const app = new StatedBeanApplication();
@@ -25,7 +22,7 @@ class InjectionFactory implements IBeanFactory {
let provide;
let provider;
if (beanDefinition.isFactoryBean) {
provide = beanDefinition.getFactoryBeanType();
provide = beanDefinition.factoryBeanType;
provider = { provide: provide, useFactory: beanDefinition.getFactory()! };
} else {
provide = beanDefinition.beanType;
@@ -46,27 +43,4 @@ class InjectionFactory implements IBeanFactory {

app.setBeanFactory(new InjectionFactory());

const App = () => {
const [counter, setCounter] = React.useState(5);
return (
<div>
<StatedBeanProvider application={app} providers={[TodoModel]}>
<button
onClick={() => {
setCounter(counter + 1);
}}
>
Add
</button>
<hr />
{counter < 20 && <Counter value={counter} />}
<hr />
{counter < 10 && <Counter value={counter} />}
<hr />
<TodoApp />
</StatedBeanProvider>
</div>
);
};

ReactDOM.render(<App />, document.getElementById('root'));
Loading