Code Monkey home page Code Monkey logo

typeless's People

Contributors

dependabot[bot] avatar lstkz avatar sisisin avatar t7yang avatar tyahha avatar yuheiy avatar yukihane 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

typeless's Issues

feature request: support middleware implementation

Motivation

I want to apply middleware for error tracking using Sentry.
When using Sentry, I want to log dispatched Actions to Sentry's Breadcrumb and get application state at the time error occurred.

workaround

Now, I think how to implement middleware like behavior is following example.
But, it is difficult to get all of store's state...

class MyRegistry extends Registry {
  dispatch(action: ActionLike) {
    // some implementation of correspond to middleware
    // ...

    super.dispatch(action);
  }
}

New version is coming

After receiving feedback from other developers, I decided to make the following design changes:

  • No redux dependency, and a concept of the global store. Each module/feature is isolated.
  • Symbol-based modules. When creating modules, a symbol is required instead of a string. It will avoid name clashing.
  • No need for root provider.
  • The only dependecy is immer. Peer dependecies are still React and RxJS.

Example counter app.

import React from 'react';
import ReactDOM from 'react-dom';
import * as Rx from 'typeless/rx';
import { createModule, useActions } from 'typeless';

/* == Module Interface == */

export const [useModule, CounterActions, getCounterState] = createModule(
  Symbol('counter')
)
  // Create Actions Creators
  .withActions({
    startCount: null, // null means no args
    countDone: (count: number) => ({ payload: { count } }),
  })
  .withState<CounterState>();

export interface CounterState {
  isLoading: boolean;
  count: number;
}

/* == Module Implementation == */

const initialState: CounterState = {
  isLoading: false,
  count: 0,
};

// Create Epic for side effects
useModule
  .epic()
  // Listen for `count` and dispatch `countDone` with 500ms delay
  .on(CounterActions.startCount, () =>
    Rx.of(CounterActions.countDone(1)).pipe(Rx.delay(500))
  );

// Create a reducer
// Under the hood it uses `immer` and state mutations are allowed
useModule
  .reducer(initialState)
  .on(CounterActions.startCount, state => {
    state.isLoading = true;
  })
  .on(CounterActions.countDone, (state, { count }) => {
    state.isLoading = false;
    state.count += count;
  });

/* == Use Module in React == */

export function Counter() {
  // load epic and reducer
  useModule();

  // wrap actions with `dispatch`
  const { startCount } = useActions(CounterActions);
  // get state from store
  const { isLoading, count } = getCounterState.useState();

  return (
    <div>
      <button disabled={isLoading} onClick={startCount}>
        {isLoading ? 'loading...' : 'increase'}
      </button>
      <div>count: {count}</div>
    </div>
  );
}

ReactDOM.render(<Counter />, document.getElementById('app'));

Example of using multiple modules in React components:

function App() {
    useXModule();
    useYModule();
    const { countX, countY } = useMappedState(
      [getXState, getYState],
      (xState, yState) => ({
        countX: xState.count,
        countY: yState.count,
      })
    );
  return <div>...</div>
}

Added createDeps to avoid duplicated code with useMappedState

const deps = createDeps({ x: getXState, y: getYState });

function App() {
    useXModule();
    useYModule();
    const { countX, countY } = deps.useMappedState(
      (state) => ({
        countX: state.x.count,
        countY: state.y.count,
      })
    );
  return <div>...</div>
}

Shared module does not work

details

When the same typeless module is used by multiple React Component, in the following procedure $unmonting ($unmounted) dispatched after $mount.
At this time, reducer and epic will be unregistered.

  1. Use typeless module (created by typeless's createModule API) in a React Component
  2. Unmount the component and mount other React Component which uses the same typeless module (unmount and mount must be triggered at the same rendering step)
  3. Then, $unmounting and $unmounted are dispatched after $mounted action.

reproduction

  • checkout this repo
  • start app and open http://localhost:3000
  • click toggle state
  • click show foo

Then, Foo/SHOW_FOO action is dispatched, but epic won't be executed.

expected behavior

In this case, run FooAction.showFoo epic normaly.

environment

typeless: 0.1.9-alpha.0 , 0.2.0
react: ^16.8.4

feature request: Allow `EpicResult` to assign `Promise<ActionLike>` and implement built-in Action: `ignore`

Feature request

  • Allow EpicResult to assign Promise<ActionLike>
  • Implement built-in Action: TypelessActions.ignore()

Motivation

Allow EpicResult to assign Promise<ActionLike>

async/await is sometimes useful.

Following example, make an Action with getSomeId's result and getSomebodyById's result using Observable pattern and Promise pattern

  .epic()
  // using Observable
  .on(SomeActions.fetchSomebody, () => {
    return Rx.from(API.getSomeId()).pipe(
      Rx.mergeMap(id =>
        Rx.from(API.getSomebodyById(id)).pipe(
          Rx.map(somebody => SomeActions.fetchSomebodyFulfilled({ id, somebody })),
        ),
      ),
      Rx.catchError(e => Rx.of(SomeActions.rejected())),
    );
  })
  // using Promise
  .on(SomeActions.fetchSomebody, async () => {
    try {
      const id = await API.getSomeId();
      const somebody = await API.getSomebodyById(id);
      return SomeActions.fetchSomebodyFulfilled({ id, somebody });
    } catch (e) {
      return SomeActions.rejected();
    }
  });

Nested Observable is too complex.
So, I want to use Promise like in this case.

Implement built-in Action: TypelessActions.ignore()

It is difficult to ignore EpicHandler which returns Promise.
So, create built-in ignore Action.

  .on(SomeActions.fetchSomebody, async () => {
    // this result type is `Promise<Observable<>>`, so this is not work!
    return Rx.empty();
  })
  .on(SomeActions.fetchSomebody, async () => {
    // this result type is Promise<ActionLike>, and works well!
    return TypelessActions.ignore();
  })

When returned TypelessActions.ignore() , it is converted Rx.empty() in createOutputStream.


I think this feature request may breaks typeless concept.
So, If you say this is not for typeless, I'll certainly withdraw this request.

Thank you.

Add type testing

Motivation

typeless has complex type inference, but has no test for types.
This causes that to fix and verify types is difficulty (e.g. #30).

So, I think typeless need to add type testing.

Proposal

sisisin@da771a7

Pros

This approach is very simple and no dependency.
Testing type is done all of tsc

Cons

Need to make all of assert functions and test structure helpers.
Developer maybe to have pain if more complex testing.

Other plan

Using tsd

Is createDeps stable API?

createDeps has no documentation.
So, I wonder it is stable API.

I think it needs following work.

  • If stable, it needs documentation
  • If unstable, add comment to code
  • If unused, delete implementation

`0.1.9-alpha.0` version should publish with beta tag.

Now, typeless 0.1.9-alpha.0 version published as latest at npm.
So, run npm install typeless, then installed 0.1.9-alhpa-0 version.

0.1.9-alpha.0 is developing now, and different from document
This causes counfusing for users, so I think to publish with beta tag.

useMappedState should prevent re-render when result got same value

useMappedState execute force re-render when Store's subscription emitted.

store.subscribe(() => subscribeRef.current())

When a Component depends on typeless module's state, performance tuning more difficulty by this behavior.

Expected behavior

It shouldn't execute force re-render when useMappedState's result got same value.

Prior art

react-redux's useSelector and reselect's createSelector are solved this problem.
Following example, I made comparison of typeless and react-redux/reselect.
code sandbox


@lstkz I'll make PR if you don't mind.

Better form library

Currently, we keep the whole state in the store, and very often it causes performance issues.
We could implement something similar to Formik, where we keep the state locally, and when the form is submitted successfully, we dispatch an action with the form values.

If someone is interested in implementing it, we can discuss the API here.

Cannot capture Error in EpicHandler

The stream created by Epic#toStream catches error on catchError operator.
This makes to capture error impossible.
I want to track error occurred Epic, but it is difficult.

My investigation

I tried to remove catchError in this line
And run following example code
Then, it is captured.

epic().on(SomeActions.occurredError, () => {
  // This error is not captured window.addEventListener("error").
  throw new Error('some error');
  return null;
});

How to fix this problem

Idea 1. Call window.dispatchEvent in catchError operator.
Idea 2. Enable to register error callback function that called in catchError operator.

How about these ideas, which is better you think?

RxJS solves this problem.
https://github.com/ReactiveX/rxjs/blob/master/src/internal/util/hostReportError.ts#L6-L8

I think this is best way, I'll submit PR.

feature request: add `computed property` in `StateGetter`

Motivation

In many case, computed value from State is used in Component and typeless Module.
To do that, declare computing function in a file, and import the function from file when use at Component or Module.
Some might directly use state values even though a computing function is already declared
So, I want API that allows to declare computing function

Proposal for API design

Add .withComputed(computed: {[key:string]: (state: TState) => any}) function in createModule().withState()'s returned value.

.withComputed function allows to add computing functions map.
.withComputed returns ComputedStateGetter function that returns state and computed property.
The state is a Store's state, computed is an object has computed values which used computing function declared withComputed function's arguments.

Example code

// interface.ts
const [handle, getState] = createModule(symbol)
  .withState<UserState>()
  .withComputed({ existsUser: state => state.user !== null })

interface UserState { user: string | null; }

// module.tsx
handle.redcer({user:null})

const UserModule = ()=>{
  handle()
  const { state, computed } = getState.useState();
  console.log(state.user, computed.existsUser); // => null, false

  return null;
}

PoC implementation: sisisin@9916ada#diff-7e4c6d666ceff158a5cf87a39e477085R104-R120

Because of this interface

  • Good type inference
    • It is inferred from declaration of computing function like .withActions function
  • Backward compatible
    • The return value of StateGetter changes only when.withComputed is called
    • But this API is inconsistent with StateGetter. StateGetter returns TState type, but ComputedStateGetter returns {state: TState}.
  • ComputedStateGetter returns nested object because works well a case of primitive value State
    • It is impossible to declare signature of getState(): TState & Computed

Prior Art

easy-peasy's computed API: https://easy-peasy.now.sh/docs/api/computed.html

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.