Skip to content

Commit

Permalink
Release 0.8.5 (#140)
Browse files Browse the repository at this point in the history
* feat: 🎸 @props field update via set method

* chore: 🤖 upgrade all dependencies

* chore: 🤖 change bundlesize

* refactor: 💡 eslint fix

* chore: buid:r error

* chore: integration with yarn-deduplicate

Co-authored-by: JounQin <admin@1stg.me>
  • Loading branch information
foreleven and JounQin committed Feb 2, 2020
1 parent e945c69 commit 795cf26
Show file tree
Hide file tree
Showing 23 changed files with 3,084 additions and 2,873 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"newline-after-var": [
"warn",
"always"
]
],
"unicorn/filename-case": 0
}
}
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ script:

after_success:
- yarn global add bundlesize codecov
- bundlesize -f lib/esm.min.js -s 4kB
- bundlesize -f lib/esm.min.js -s 5kB
- codecov

before_deploy: yarn build:example --public-url /$TRAVIS_REPO_NAME/
Expand Down
13 changes: 13 additions & 0 deletions example/dev.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Playground</title>
</head>
<body>
<div id="root"></div>
<script src="./dev.tsx"></script>
</body>
</html>
8 changes: 8 additions & 0 deletions example/dev.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'reflect-metadata';

import React from 'react';
import ReactDOM from 'react-dom';

import { DevApp } from './src/Dev';

ReactDOM.render(<DevApp />, document.querySelector('#root'));
2 changes: 1 addition & 1 deletion example/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import ReactDOM from 'react-dom';

import { App } from './src/App';

ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(<App />, document.querySelector('#root'));
6 changes: 3 additions & 3 deletions example/src/Test.tsx → example/src/Dev.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react';

import { Counter } from './components/Counter';
import { CounterSpeed } from './components/Counter';

import { StatedBeanApplication, StatedBeanProvider } from 'stated-bean';

const app = new StatedBeanApplication();

export const Test = () => {
export const DevApp = () => {
return (
<StatedBeanProvider application={app}>
<Counter value={10} />
<CounterSpeed />
</StatedBeanProvider>
);
};
36 changes: 19 additions & 17 deletions example/src/components/Counter/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
/* eslint-disable @typescript-eslint/unbound-method */
import React from 'react';

import { CounterModel } from '../../models/CounterModel';
import { CounterModel } from './model';

import { useBean } from 'stated-bean';

export interface CounterProps {
value: number;
speed?: number;
}

// const CounterModel = {
// count: 10,
// decrement() {
// this.count--;
// },
// increment() {
// this.count++;
// },
// };
export function Counter(props: CounterProps = { speed: 1 }) {
const counter = useBean(CounterModel, { props });

export function Counter(props: CounterProps) {
const counter = useBean(() => new CounterModel(), { props });
return <span>Count: {counter.count}</span>;
}

export function CounterSpeed() {
const [speed, setSpeed] = React.useState(1);

return (
<div>
<button onClick={counter.decrement}>-</button>
<span>{counter.count}</span>
<button onClick={counter.increment}>+</button>
<div style={{ margin: '50px auto', display: 'flex', justifyContent: 'center' }}>
<span>Speed: </span>
<input
type="number"
value={speed}
onChange={e => {
setSpeed(Number(e.target.value));
}}
/>
<Counter speed={speed} />
</div>
);
}
32 changes: 32 additions & 0 deletions example/src/components/Counter/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { InitializingBean, Props, Stated, StatedBean } from 'stated-bean';

@StatedBean({ singleton: false })
export class CounterModel implements InitializingBean {
@Stated()
count = 0;

@Props()
speed = 1;

timer?: NodeJS.Timeout;

afterProvided() {
this.startTimer(1);
}

startTimer(speed: number) {
if (this.timer) {
clearInterval(this.timer);
}

this.timer = setInterval(() => {
this.count++;
}, 1000 * (1 / speed));
}

setSpeed(speed: number) {
if (speed > 0) {
this.startTimer(speed);
}
}
}
3 changes: 2 additions & 1 deletion example/src/components/Todo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';

import { TodoModel } from '../../models/TodoModel';
import { Todo } from '../../services/TodoService';

import { TodoModel } from './model';

import { useInject, useObserveEffect } from 'stated-bean';

function TodoList(props: { items: Todo[] }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Injectable, Inject } from 'injection-js';
import { Inject, Injectable } from 'injection-js';

import { TodoService, Todo } from '../services/TodoService';
import { Todo, TodoService } from '../../services/TodoService';

import { StatedBean, Stated, AfterProvided, Effect, DisposableBean } from 'stated-bean';
import { AfterProvided, DisposableBean, Effect, Stated, StatedBean } from 'stated-bean';

@StatedBean()
@Injectable()
Expand Down
26 changes: 0 additions & 26 deletions example/src/models/CounterModel.ts

This file was deleted.

35 changes: 19 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stated-bean",
"version": "0.8.4",
"version": "0.8.5",
"description": "A light but scalable state management library with react hooks",
"repository": "git@github.com:mjolnirjs/stated-bean.git",
"license": "MIT",
Expand All @@ -11,17 +11,19 @@
"unpkg": "lib/umd.min.js",
"types": "lib/types/index.d.ts",
"files": [
"lib"
"lib",
"!*.tsbuildinfo"
],
"scripts": {
"build": "rimraf dist lib && run-p build:**",
"build:example": "parcel build ./example/index.html",
"build:r": "r -p",
"build:ts": "tsc -p src",
"dev": "parcel ./example/index.html --https",
"dev": "parcel ./example/dev.html --https",
"lint": "run-p lint:*",
"lint:es": "cross-env EFF_NO_LINK_RULES=true eslint . --cache --ext js,md,mdx,ts,tsx -f friendly --fix",
"lint:tsc": "tsc --noEmit",
"postinstall": "yarn-deduplicate || exit 0",
"serve": "serve dist",
"start": "yarn dev",
"test": "jest",
Expand All @@ -33,27 +35,28 @@
"tslib": ">=1.0.0"
},
"devDependencies": {
"@1stg/lib-config": "^0.1.13",
"@1stg/lib-config": "^0.3.0",
"@testing-library/react-hooks": "^3.2.1",
"@types/jest": "^24.0.22",
"@types/node": "^12.12.7",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4",
"@types/react-test-renderer": "^16.9.0",
"injection-js": "^2.2.2",
"@types/jest": "^25.1.1",
"@types/node": "^13.7.0",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/react-test-renderer": "^16.9.2",
"injection-js": "^2.3.0",
"less": "^3.10.3",
"npm-run-all": "^4.1.5",
"parcel-bundler": "^1.12.3",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-test-renderer": "^16.11.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0",
"rxjs": "^6.5.3",
"serve": "^11.2.0",
"ts-jest": "^24.1.0",
"type-coverage": "^2.3.0",
"typescript": "^3.7.2"
"rimraf": "^3.0.1",
"rxjs": "^6.5.4",
"serve": "^11.3.0",
"ts-jest": "^25.1.0",
"type-coverage": "^2.4.0",
"typescript": "^3.7.5",
"yarn-deduplicate": "^1.1.1"
},
"sideEffects": false,
"alias": {
Expand Down
15 changes: 11 additions & 4 deletions src/core/BeanObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CountableSubject } from './CountableSubject';
import { isBeanContainerAware, isDisposableBean, isInitializingBean } from './LifeCycle';
import { StatedBeanContainer } from './StatedBeanContainer';
import { StatedBeanWrapper } from './Symbols';
import { shallowEqual } from './shallowEqual';

export class BeanObserver<T = unknown> {
state$: CountableSubject<StateAction<T>> = new CountableSubject();
Expand Down Expand Up @@ -157,18 +158,24 @@ export class BeanObserver<T = unknown> {
}

private _updatePropsField(bean: T, field: PropsFieldMeta, props?: Record<string, unknown>) {
const target = (bean as unknown) as object;
const newValue = props === undefined ? undefined : props[field.prop];
const oldValue = Reflect.get((bean as unknown) as object, field.name);
const oldValue = Reflect.get(target, field.name);

if (field.observable) {
const subject = oldValue as BehaviorSubject<unknown>;

if (!Object.is(subject.getValue(), newValue)) {
if (!shallowEqual(subject.getValue(), newValue)) {
subject.next(newValue);
}
} else {
if (!Object.is(oldValue, newValue)) {
Reflect.set((bean as unknown) as object, field.name, newValue);
if (!shallowEqual(oldValue, newValue)) {
const propsName = String(field.name);
const setter = Reflect.get(target, 'set' + propsName.charAt(0).toUpperCase() + propsName.slice(1));

if (setter && typeof setter === 'function') {
setter.apply(bean, [newValue]);
}
}
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/core/shallowEqual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* istanbul ignore file */

function is(x: unknown, y: unknown): boolean {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
// Added the nonzero y check to make Flow happy, but it is redundant
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
// eslint-disable-next-line no-self-compare
return x !== x && y !== y;
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function shallowEqual(objA: any, objB: any): boolean {
if (is(objA, objB)) {
return true;
}

if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}

const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

if (keysA.length !== keysB.length) {
return false;
}

// Test for A's keys different from B.
for (const element of keysA) {
if (!Object.prototype.hasOwnProperty.call(objB, element) || !is(objA[element], objB[element])) {
return false;
}
}

return true;
}

export { shallowEqual };
2 changes: 1 addition & 1 deletion src/decorator/ObservableProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function ObservableProps(name?: string): PropertyDecorator {
if (prop === undefined) {
prop = String(propertyKey);
if (prop.endsWith('$')) {
prop = prop.substring(0, prop.length - 1);
prop = prop.slice(0, Math.max(0, prop.length - 1));
}
}
getMetadataStorage().collectPropsField({
Expand Down
13 changes: 11 additions & 2 deletions src/hooks/useBean.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useContext, useEffect, useState } from 'react';
import { useCallback, useContext, useDebugValue, useEffect, useState } from 'react';

import { getStatedBeanContext } from '../context';
import { BeanDefinition } from '../core';
import { BeanDefinition, BeanObserver } from '../core';
import { BeanProvider, ClassType, StateAction } from '../types';
import { isFunction, isStatedBeanClass } from '../utils';

Expand Down Expand Up @@ -32,6 +32,7 @@ export function useBean<T, TProps = Record<string, unknown>>(
): T {
const StateBeanContext = getStatedBeanContext();
const context = useContext(StateBeanContext);

const [, setVersion] = useState(0);

let name: string | symbol | undefined;
Expand Down Expand Up @@ -82,5 +83,13 @@ export function useBean<T, TProps = Record<string, unknown>>(
return () => subscription.unsubscribe();
}, [subscription]);

useDebugValue(observer, (ob: BeanObserver<T>) => {
return {
metadata: ob.beanMeta,
bean: ob.origin,
id: `${ob.beanDefinition.beanType.name}#${String(ob.beanDefinition.beanName)}`,
};
});

return observer.proxy;
}
Loading

0 comments on commit 795cf26

Please sign in to comment.