Giter Site home page Giter Site logo

frp-ts's People

Contributors

coolassassin avatar dependabot[bot] avatar fyzu avatar prodderman avatar raveclassic avatar tamazlykar 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

frp-ts's Issues

combine function for property

@raveclassic, hi!

Looks like typescript is finally ready to express combine function using tuple types:

type PropertyValue<A> = A extends Property<infer U> ? U : never;
type MapPropsToValues<PS extends [Property<unknown>, ...Property<unknown>[]]> = {
  [K in keyof PS]: PropertyValue<PS[K]>;
};

function combine<PS extends [Property<unknown>, ...Property<unknown>[]], B>(
  ...args: [...PS, (...values: MapPropsToValues<PS>) => B]
): Property<B>;

Correctly inferred types:

const p1 = constProperty(1);
const p2 = constProperty('');
const p3 = constProperty(true);
const p4 = constProperty(new Date());
const pr = combine(p1, p2, p3, p4, (v1, v2, v3, v4) => {
  return { name: 'Bar' };
});

image

Would you accept a PR that adds combine to property API?

`Property` example in README fails to compile

One of the examples provided in the readme fails to compile. The following snippet is copy pasted directly from the readme.

import { newAtom, Property } from "@frp-ts/core"

interface Counter extends Property<number> {
  readonly inc: () => void
}

const newCounter = (initial: number): Counter => {
  const state = newAtom(initial)
  const inc = () => state.modify((n) => n + 1)

  return {
    subscribe: state.subscribe,
    get: state.get,
    inc,
  }
}

The snippet above fails to compile with the following error

Property '[Symbol.observable]' is missing in type '{ subscribe: (observer: Observer<number>) => Subscription; get: () => number; inc: () => void; }' but required in type 'Counter'.

Versions are as follows

"dependencies": {
    "@frp-ts/core": "^1.0.0-alpha.13",
    "@frp-ts/fp-ts": "^1.0.0-alpha.13",
    "@frp-ts/lens": "^1.0.0-alpha.13",
    "frp-ts": "^0.0.1"
  },
  "devDependencies": {
    "typescript": "^4.5.5"
  }

[Question] Is it really beneficial to have implicit filtering logic on value set?

Hi @raveclassic !
I see that atoms has built-in deduping logic for referentially equal values:

const set = (a: A): void => {
if (last !== a) {
last = a
e.next(env.clock.now())
}
}

while i do understand what this made for and most of the time it's very convenient to skip updates for same values, but there are cases where this behavior might be really unwanted.
As for example one can use atoms for some event's payload processing where every single value counts despite the fact they can be identical.

Perhaps it's worth to move out filtering logic to dedicated property operator?

Add LICENSE

For source code license to work correctly, it's worth adding a file LICENSE describing the type of license and who has these rights.

Optimise construction of observer in mergeMany combinator

Currently we create a new Observer for each dependent Observable in mergeMany. This can be optimized to creating a single Observer for all Observables as it's only bound to lastNotifiedTime and listener.
Additionally, it might make sense to start lastNotifiedTime with -Infinity instead of Infinity.

Link NPM packages to repository

To see which packages are published within the repository and which latest versions are available, need to link existed packages to github repository.

[Question] Duped values - do not propagate events or simply return cached value

Hi there!
I have question regarding proper approach on working with properties which has duped value. How to obtain them when atoms have deduping logic on set? Well pretty easy - just put some complex data structure into atom (e.g. object) and then derive property with primitive value.
So what confusing me is that previously (in 0.0.x versions) we had some operators like map:

frp-ts/src/property.ts

Lines 59 to 65 in 8c97587

map: (fa, f) => {
const memoF = memo1(f)
return {
get: () => memoF(fa.get()),
subscribe: fa.subscribe,
}
},

where incorporated memoizing would do what it's intended to do - skipping re-evaluation of provided fn on same arg, not stopping propagating value down the chain.

Now I see a bit different technique involved in combine operator (it's essentially the same as map):

next: (time) => {
const newValue = get()
if (lastValue !== newValue) {
lastValue = newValue
observer.next(time)
}
},

so now combine would just skip propagating event so observers will not be notified in case of duped value.

Earlier I was thinking that deduplication/filtering is in full responsibility of value producer and should happen right before setting it to atom (except for builtin triple eq checking in atom.set), but now I'm not sure that this approach was right and it's looks like it's totally ok to pull-down circuit breaker somewhere along the way of evaluation chain.

@raveclassic @Fyzu can you share your thoughts please?

usePropertyFromProps inconsistency

Hi @raveclassic!

There is a problem with usePropertyFromProps - the atom changes after the render and at the moment of the react props change prop !== prop$.get():

const Component = ({ prop }) => {
  const prop$ = usePropertyFromProps(prop)
  
  prop$.get() === prop // false at the moment of the props change 
}

Isn't it better to use the following implementation:

function usePropertyFromProps<Value>(value: Value): Property<Value> {
  const atomRef = useRef(newAtom(value))
  atomRef.current.set(value)
  return atomRef.current
}

Unstable default clock

Hey @raveclassic!

I've noticed that there possibility of data loss since default clock is always incrementing inner value either it's overflows or not:

now: () => ++time,

While it's pretty uncommon to have such a large amount of updates it is still preferable to have handling logic of big numbers.

feat: compatible with svelte by default

Frp almost completely compatible with svelte, except for a moment with a subscription.

Svelte uses simple store contract.

type Store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }

It seems to me that you can make a separate method for subscribing for a Time like .watch(time => {}) for internal usage, and adopt subscribe to .subscribe(value => {}).

This way we will get compatibility with svelte and it will be more understandable for users who want to work with frp as a reactive library.

Prefer using react api for useProperty hook

Hey, @raveclassic!

Intro

I'm developing a chat. It means that I need to subscribe to properties in leaves (messages, etc.) so only necessary parts re-render.
I was testing performance and noticed that old version of useProperty lost updates (new works fine). This led me to some research. Results are below.

1 React-way to subscribe to mutable sources

Look like there are three versions of "how to subscribe to external mutable source" in React:

  1. createSubscription + useSubscription
  2. useMutableSource (was renamed to the below)
  3. useSyncExternalStore (package)

It turns out that managing frp-ts like source is not that easy in React because you have to take into account SSR, Suspense and async rendering (coming in React 18).

My suggestion is to use useSyncExternalStore in frp-ts/react package so the React team handles all the details (I'll attach reading list at the end for a better context).

2 State modification during render

I know that this is an anti-pattern and it should not be done (react repo issue). But, now I wonder whether this still can happen in large codebases (sockets, event handlers, whatever).

React team introduces StrictEffects and this should catch the above. Until that lands, I wonder whether deferring state.set calls as done in most eliminates the problem completely.

I still need to wrap my head around this. Maybe this case will never happen in a real application. Maybe the only way to trigger this is to directly modify the state in a render function.

Two demos showcasing this:

3 Further read

Show

Packages:

API Discussions:

react-redux migration to useSES:

mobx issues:

New version of the useProperty hook

I tested my component with the hook below and everything seems to work fine.

const bridge = <A,>(
  p: Property<A>,
): {
  subscribe: (onStoreChange: () => void) => () => void;
  getSnapshot: () => A;
} => ({
  getSnapshot: () => p.get(),
  subscribe: (onChange) => {
    const sub = p.subscribe({
      next: () => onChange(),
    });
    return () => sub.unsubscribe();
  },
});

const useProperty = <A,>(p: Property<A>): A => {
  const { getSnapshot, subscribe } = useMemo(() => bridge(p), [p]);
  return useSyncExternalStore(subscribe, getSnapshot);
};

Summary

  1. Looks like useSyncExternalStore is the way to go for frp-ts React integration.
  2. There is a potential issue with mid-render updates, maybe there is a smart way to prevent\fix that (not necessarily in frp-ts).
  3. Maybe there is some works needed to prepare for React 18.

feat: distinctUntilChanged operator

Hi @raveclassic. Thanks for your lib.
I have a next example of usage of your library and wanna propose an operator for this.
const sortedElements = property.combine(data, elements, sortElements);
I have an array of some elements that should be sorted based on data. So when the data is updates i recalculate a new sortedElements, and it may have same order but different reference. So i need an ability to have a custom comparator function to omit some of the Property changes.

Looks like a distinctUntilChanged operator from rxjs can be a good fit for this, but with mandatory comparator, because frp-ts not emit when values has same reference.
I make an implementation of this function as proposal for example #58. So maybe it will be helpful for other devs too.
Can you take a look?

@frp-ts/state addition

@raveclassic, hi!

Would you accept the below feature as a separate frp-ts package\addition to the core package?

// API

type StateActions<S> = Record<string, (this: S, ...args: any[]) => undefined | void>;

type OmitThis<AS extends StateActions<any>> = {
  [K in keyof AS]: AS[K] extends (this: any, ...args: infer U) => void | undefined ? (...args: U) => void : never;
};

export const newState = <S, AS extends StateActions<S>>(initial: S, actions: AS): Property<S> & OmitThis<AS> => {
  throw Error('impl is skipped');
};

// USAGE

interface Dog {
  name: string;
}
interface House {
  dog: Dog;
}
interface AppState {
  house: House;
}

const initialState: AppState = { house: { dog: { name: 'Fido' } } };

const store = newState(initialState, {
  renameTheDog(newName: string) {
    // this is typed correctly : AppState
    this.house.dog.name = newName;
  },
});

// typed correctly, this is omitted
store.renameTheDog('Odif');

Internally it will use immer to support concise mutation syntax.

Motivation

I find it convenient to group store\vm state into a single object and expose a single atom in the API. This leads to some boilerplate when you need to modify parts of the state.
Immer helps a lot, but we still need to use modify calls. This change simplifies this use case.

Besides, some libraries provide this out of the box (SolidJS as example).

perf files contain ts errors

There are some errors in VSCode in perf folder:

source.spec.ts
image

emitter.spec.ts
image

Is it indeed the case or there is a problem with my local setup?

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.