LeetCode-OpenSource/ayanami

Support for React Native ?

LyunKi opened this issue · 16 comments

I tried to use ayanami in RN but failed. Can ayanami support rn or is there any plan to adapt rn...

ayanami required TypeScript, could you provide tsconfig.json in your project?

1D220F20-835B-462F-BE94-9039079E42D5

{
    "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "jsx": "react-native",
        "lib": ["dom", "es2015"],
        "moduleResolution": "node",
        "noEmit": true,
        "skipLibCheck": true,
        "resolveJsonModule": true,
        "baseUrl": ".",
        "paths": {
            "@/*": ["./src/*"]
        },
        "declaration": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "strict": true,
        "noUnusedParameters": true,
        "noUnusedLocals": true,
        "noImplicitAny": true,
        "noImplicitReturns": true,
        "module": "commonjs",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5"
    }
}
import React, { useCallback } from 'react';
import { Ayanami, Effect, Reducer, useAyanami, useAyanamiState, Module } from 'ayanami';
import { of, Observable } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { View, Text, Button } from 'react-native';

interface State {
    count: number;
}

interface TipsState {
    tips: string;
}

@Module('Tips')
class Tips extends Ayanami<TipsState> {
    defaultState = {
        tips: '',
    };

    @Reducer()
    showTips(state: TipsState, tips: string): TipsState {
        return { ...state, tips };
    }
}

@Module('Count')
class Count extends Ayanami<State> {
    defaultState = {
        count: 0,
    };

    otherProps = ''

    constructor(private readonly tips: Tips) {
        super();
    }

    @Reducer()
    add(state: State, count: number): State {
        return { count: state.count + count };
    }

    @Reducer()
    addOne(state: State): State {
        return { count: state.count + 1 };
    }

    @Reducer()
    reset(): State {
        return { count: 0 };
    }

    @Effect()
    minus(count$: Observable<number>): any {
        return count$.pipe(
            mergeMap((subCount) =>
                of(
                    this.getActions().add(-subCount),
                    this.tips.getActions().showTips(`click minus Button at ${Date.now()}`)
                )
            )
        );
    }
}

function CountComponent() {
    const [{ count }, actions] = useAyanami(Count);
    const { tips } = useAyanamiState(Tips);

    const add = useCallback((count: number) => () => actions.add(count), []);
    const minus = useCallback((count: number) => () => actions.minus(count), []);
    const reset = useCallback(() => {
        actions.reset();
    }, []);

    return (
        <View>
            <Text>count: {count}</Text>
            <Text>tips: {tips}</Text>
            <Button title={'123'} onPress={add(1)}>
                add one
            </Button>
            <Button title={'234'} onPress={minus(1)}>
                minus one
            </Button>
            <Button title={'345'} onPress={reset}>
                reset to zero
            </Button>
        </View>
    );
}

export default function App() {
    return <CountComponent />;
}

@LyunKi sorry, the documents for latest versions is still in developing.
Add InjectableContext in you App component should fix your problem:

import { InjectableContext } from 'ayanami'


export default function App() {
    return <InjectableContext><CountComponent /></InjectableContext>;
}

@LyunKi sorry, the documents for latest versions is still in developing.
Add InjectableContext in you App component should fix your problem:

import { InjectableContext } from 'ayanami'


export default function App() {
    return <InjectableContext><CountComponent /></InjectableContext>;
}

我用中文表达吧 - 。 -应该更能传达意思,加了之后,di 还是失败了,提示 "cannot resolve all parameters for Count(?).xxxx".但我把 Count 对于 Tip的依赖去掉后,程序确实运行成功了。下面会放上我的代码,麻烦能看一下

import React, { useCallback } from 'react';
import {
    Ayanami,
    Effect,
    Reducer,
    useAyanami,
    useAyanamiState,
    Module,
    InjectableContext,
} from 'ayanami';
import { of, Observable } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { View, Text, Button } from 'react-native';

interface State {
    count: number;
}

interface TipsState {
    tips: string;
}

@Module('Tips')
class Tips extends Ayanami<TipsState> {
    defaultState = {
        tips: '',
    };

    @Reducer()
    showTips(state: TipsState, tips: string): TipsState {
        return { ...state, tips };
    }
}

@Module('Count')
class Count extends Ayanami<State> {
    defaultState = {
        count: 0,
    };

    otherProps = '';

    //将这一部分代码去掉后能运行成功
    constructor(private readonly tips: Tips) {
        super();
    }

    @Reducer()
    add(state: State, count: number): State {
        return { count: state.count + count };
    }

    @Reducer()
    addOne(state: State): State {
        return { count: state.count + 1 };
    }

    @Reducer()
    reset(): State {
        return { count: 0 };
    }

    @Effect()
    minus(count$: Observable<number>): any {
        return count$.pipe(
            mergeMap((subCount) =>
                of(
                    this.getActions().add(-subCount),
                    this.tips.getActions().showTips(`click minus Button at ${Date.now()}`)
                )
            )
        );
    }
}

function CountComponent() {
    const [{ count }, actions] = useAyanami(Count);
    const { tips } = useAyanamiState(Tips);

    const add = useCallback((count: number) => () => actions.add(count), []);
    const minus = useCallback((count: number) => () => actions.minus(count), []);
    const reset = useCallback(() => {
        actions.reset();
    }, []);

    return (
        <View>
            <Text>count: {count}</Text>
            <Text>tips: {tips}</Text>
            <Button title={'123'} onPress={add(1)}>
                add one
            </Button>
            <Button title={'234'} onPress={minus(1)}>
                minus one
            </Button>
            <Button title={'345'} onPress={reset}>
                reset to zero
            </Button>
        </View>
    );
}

export default function App() {
    return (
        <InjectableContext>
            <CountComponent />
        </InjectableContext>
    );
}

Cannot resolve all parameters for 'Count'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'Count' is decorated with Injectable.

@LyunKi ayanami@rc and @asuka/di is still in heavy developing. I will ensure this issue resolved when I finished develop.

@LyunKi ayanami@rc and @asuka/di is still in heavy developing. I will ensure this issue resolved when I finished develop.

thanks for your feedback,I think ayanami looks elegant and I hope to use it as my new state management tool for learning rxjs by the way = . =

@LyunKi try 1.0.0-rc.5

@LyunKi try 1.0.0-rc.5

thx,I'll try it

@LyunKi try 1.0.0-rc.5

emmm,when I tried it ,it could run as what I wish ,but when I triggered the minus function which would dispatch action in Tip modules,it crashed with error message

TypeError: undefined is not an object (evaluating '_this3.tips.getActions')

after reading the docs ,I felt confused with follows;

  1. It seems that I have no need to add ? I tried to add it or remove it ,but with nothing changes
  2. the main entry port in expo is "main": "node_modules/expo/AppEntry.js",it should be blamed for the failed DI?...
import 'reflect-metadata';

import {
    Ayanami,
    Effect,
    initDevtool,
    Module,
    Reducer,
    useAyanami,
    useAyanamiState,
} from 'ayanami';
import React, { useCallback } from 'react';
import { Button, Text, View } from 'react-native';
import { Observable, of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

interface State {
    count: number;
}

interface TipsState {
    tips: string;
}

@Module('Tips')
class Tips extends Ayanami<TipsState> {
    defaultState = {
        tips: '123',
    };

    @Reducer()
    showTips(state: TipsState, tips: string): TipsState {
        return { ...state, tips };
    }
}

@Module('Count')
class Count extends Ayanami<State> {
    defaultState = {
        count: 0,
    };

    otherProps = '';

    constructor(private readonly tips: Tips) {
        super();
    }

    @Reducer()
    add(state: State, count: number): State {
        return { count: state.count + count };
    }

    @Reducer()
    addOne(state: State): State {
        return { count: state.count + 1 };
    }

    @Reducer()
    reset(): State {
        return { count: 0 };
    }

    @Effect()
    minus(count$: Observable<number>): any {
        return count$.pipe(
            mergeMap((subCount) =>
                of(
                    this.getActions().add(-subCount),
                    this.tips.getActions().showTips(`click minus Button at ${Date.now()}`)
                )
            )
        );
    }
}

function CountComponent() {
    const [{ count }, actions] = useAyanami(Count);
    const { tips } = useAyanamiState(Tips);

    const add = useCallback((count: number) => () => actions.add(count), [actions]);
    const minus = useCallback((count: number) => () => actions.minus(count), [actions]);
    const reset = useCallback(() => {
        actions.reset();
    }, [actions]);

    return (
        <View>
            <Text>count: {count}</Text>
            <Text>tips: {tips}</Text>
            <Button title={'123'} onPress={add(1)}>
                add one
            </Button>
            <Button title={'234'} onPress={minus(1)}>
                minus one
            </Button>
            <Button title={'345'} onPress={reset}>
                reset to zero
            </Button>
        </View>
    );
}

initDevtool();

export default function App() {
    return <CountComponent />;
}

@LyunKi could you please upload a minimal repo to let me reproduce this bug?

@LyunKi could you please upload a minimal repo to let me reproduce this bug?

as your wish~ https://github.com/LyunKi/example
could you please reopen this issue..

@LyunKi
TLDR; just add plugins: ["babel-plugin-transform-typescript-metadata"] in babel.config.js.

The issue in your example is that expo use babel to precess TypeScript files, which won't follow tsconfig --emitDecoratorMetadata true to emit parameters types required by DI.

For more information, please see:
babel/babel#9681
https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options

@LyunKi
TLDR; just add plugins: ["babel-plugin-transform-typescript-metadata"] in babel.config.js.

The issue in your example is that expo use babel to precess TypeScript files, which won't follow tsconfig --emitDecoratorMetadata true to emit parameters types required by DI.

For more information, please see:
babel/babel#9681
https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options

thx~I had tried your solution and the problem had been fixed