From a038ba303947598f3f79a03d994d97d5db3eb2e7 Mon Sep 17 00:00:00 2001 From: JeffJiang Date: Sat, 7 Mar 2020 14:28:45 +0800 Subject: [PATCH] Release/0.9.0 (#154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 🐛 update props field when change * feat: 🎸 useObserveEffect support function --- example/src/Dev.tsx | 33 ++++++++++++++++++++++++++- example/src/components/Todo/index.tsx | 7 +++--- package.json | 2 +- src/core/BeanObserver.ts | 1 + src/decorator/Effect.ts | 5 ++-- src/hooks/useObserveEffect.ts | 7 +++--- src/types/Actions.ts | 1 + src/types/ClassType.ts | 5 ++++ test/effect-action.test.tsx | 2 +- test/props.test.tsx | 2 +- 10 files changed, 53 insertions(+), 12 deletions(-) diff --git a/example/src/Dev.tsx b/example/src/Dev.tsx index 8f218c783..35384e837 100644 --- a/example/src/Dev.tsx +++ b/example/src/Dev.tsx @@ -1,15 +1,46 @@ +import { ReflectiveInjector } from 'injection-js'; import React from 'react'; import { CounterSpeed } from './components/Counter'; +import { TodoApp } from './components/Todo'; +import { TodoService } from './services/TodoService'; -import { StatedBeanApplication, StatedBeanProvider } from 'stated-bean'; +import { StatedBeanApplication, StatedBeanProvider, IBeanFactory, BeanDefinition } from 'stated-bean'; const app = new StatedBeanApplication(); +class InjectionFactory implements IBeanFactory { + rootInjector = ReflectiveInjector.resolveAndCreate([TodoService]); + + createBean(beanDefinition: BeanDefinition) { + let provide; + let provider; + + if (beanDefinition.isFactoryBean) { + provide = beanDefinition.factoryBeanType; + provider = { provide: provide, useFactory: beanDefinition.getFactory()! }; + } else { + provide = beanDefinition.beanType; + provider = { provide: provide, useClass: beanDefinition.beanType }; + } + const injector = ReflectiveInjector.resolveAndCreate([provider], this.rootInjector); + + return injector.get(provide); + } + + destroyBean(beanDefinition: BeanDefinition) { + console.info('destroyed', beanDefinition); + } +} + +app.setBeanFactory(new InjectionFactory()); + export const DevApp = () => { return ( + + ); }; diff --git a/example/src/components/Todo/index.tsx b/example/src/components/Todo/index.tsx index 438049013..7a0eeefea 100644 --- a/example/src/components/Todo/index.tsx +++ b/example/src/components/Todo/index.tsx @@ -4,7 +4,7 @@ import { Todo } from '../../services/TodoService'; import { TodoModel } from './model'; -import { useInject, useObserveEffect } from 'stated-bean'; +import { useBean, useObserveEffect } from 'stated-bean'; function TodoList(props: { items: Todo[] }) { return ( @@ -17,8 +17,9 @@ function TodoList(props: { items: Todo[] }) { } export const TodoApp = () => { - const todo = useInject(TodoModel); - const { loading, error } = useObserveEffect(todo, 'fetchTodo'); + const todo = useBean(TodoModel); + // eslint-disable-next-line @typescript-eslint/unbound-method + const { loading, error } = useObserveEffect(todo, todo.fetchTodo); console.log(loading, error, todo.todoList); return ( diff --git a/package.json b/package.json index b9eae443c..3396ee341 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stated-bean", - "version": "0.8.6", + "version": "0.9.0", "description": "A light but scalable state management library with react hooks", "repository": "git@github.com:mjolnirjs/stated-bean.git", "license": "MIT", diff --git a/src/core/BeanObserver.ts b/src/core/BeanObserver.ts index 113a00c29..52d54f3b9 100644 --- a/src/core/BeanObserver.ts +++ b/src/core/BeanObserver.ts @@ -178,6 +178,7 @@ export class BeanObserver { } else { Reflect.set((bean as unknown) as object, field.name, newValue); } + Reflect.set((bean as unknown) as object, field.name, newValue); } } } diff --git a/src/decorator/Effect.ts b/src/decorator/Effect.ts index 7390c11fd..5442b82e6 100644 --- a/src/decorator/Effect.ts +++ b/src/decorator/Effect.ts @@ -6,7 +6,7 @@ import { getBeanWrapper, isPromise } from '../utils'; * @export * @returns {MethodDecorator} */ -export function Effect(name?: string | symbol): MethodDecorator { +export function Effect(): MethodDecorator { return ( prototype, propertyKey, @@ -16,7 +16,7 @@ export function Effect(name?: string | symbol): MethodDecorator { if (descriptor === undefined) { descriptor = Object.getOwnPropertyDescriptor(prototype, propertyKey)!; } - const effectName = name || propertyKey; + const effectName = propertyKey; const originalMethod: Function = descriptor.value; descriptor.value = function(this: T, ...args: unknown[]) { @@ -29,6 +29,7 @@ export function Effect(name?: string | symbol): MethodDecorator { if (observer !== undefined) { observer.effect$.next({ effect: effectName, + effectTarget: Reflect.get((this as unknown) as object, effectName), ...action, } as EffectAction); } diff --git a/src/hooks/useObserveEffect.ts b/src/hooks/useObserveEffect.ts index fba22805c..726910d9c 100644 --- a/src/hooks/useObserveEffect.ts +++ b/src/hooks/useObserveEffect.ts @@ -1,7 +1,7 @@ import { useCallback, useContext, useEffect, useState } from 'react'; import { getStatedBeanContext } from '../context'; -import { EffectAction, FunctionPropertyNames } from '../types'; +import { EffectAction, FunctionPropertyNames, FunctionProperty } from '../types'; import { getBeanWrapper } from '../utils'; /** @@ -13,7 +13,7 @@ import { getBeanWrapper } from '../utils'; * @param {FunctionPropertyNames} effect * @returns {EffectAction} */ -export function useObserveEffect(bean: T, effect: FunctionPropertyNames | string | symbol): EffectAction { +export function useObserveEffect(bean: T, effect: FunctionPropertyNames | FunctionProperty): EffectAction { const StateBeanContext = getStatedBeanContext(); const context = useContext(StateBeanContext); const container = context.container; @@ -32,7 +32,8 @@ export function useObserveEffect(bean: T, effect: FunctionPropertyNames | const listener = useCallback( (action: EffectAction) => { - if (action.effect === effect) { + console.log(action.effectTarget === effect); + if (action.effect === effect || action.effectTarget === effect) { setEffectState(action); } }, diff --git a/src/types/Actions.ts b/src/types/Actions.ts index 6f1ea260b..b37dd979c 100644 --- a/src/types/Actions.ts +++ b/src/types/Actions.ts @@ -12,6 +12,7 @@ export interface EffectAction { data: T; error: unknown; effect: string | symbol; + effectTarget?: Function; } export interface LifeCycleAction { diff --git a/src/types/ClassType.ts b/src/types/ClassType.ts index 148e8d94b..da00b2647 100644 --- a/src/types/ClassType.ts +++ b/src/types/ClassType.ts @@ -8,3 +8,8 @@ export type InstanceType> = T extends ClassType export type FunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]; + +// eslint-disable-next-line @typescript-eslint/no-type-alias +export type FunctionProperty = { + [K in keyof T]: T[K] extends Function ? T[K] : never; +}[keyof T]; diff --git a/test/effect-action.test.tsx b/test/effect-action.test.tsx index 5424b7e74..b54f7d0c8 100644 --- a/test/effect-action.test.tsx +++ b/test/effect-action.test.tsx @@ -11,7 +11,7 @@ class PostProvidedSample { @Stated() test = 0; - @Effect('add') + @Effect() async add() { await delay(100); this.test += 1; diff --git a/test/props.test.tsx b/test/props.test.tsx index 9a4c63a27..090ee9c45 100644 --- a/test/props.test.tsx +++ b/test/props.test.tsx @@ -45,7 +45,7 @@ describe('props observer test', () => { rerender({ value: 20 }); - expect(result.current.value3).toBe(10); + expect(result.current.value3).toBe(20); expect(result.current.value4).toBe(20); expect(result.current.value.getValue()).toBe(20); expect(result.current.value$.getValue()).toBe(20);