Code Monkey home page Code Monkey logo

patronum's Introduction

Effector Comet Logo


join gitter rate on openbase build status discord chat become a patron

☄️ effector

Business logic with ease

Visit effector.dev for docs, guides and examples

Table of Contents

Introduction

Effector implements business logic with ease for Javascript apps (React/React Native/Vue/Svelte/Node.js/Vanilla), allows you to manage data flow in complex applications. Effector provides best TypeScript support out of the box.

Effector follows five basic principles:

  • Application stores should be as light as possible - the idea of adding a store for specific needs should not be frightening or damaging to the developer.
  • Application stores should be freely combined - data that the application needs can be statically distributed, showing how it will be converted in runtime.
  • Autonomy from controversial concepts - no decorators, no need to use classes or proxies - this is not required to control the state of the application and therefore the api library uses only functions and plain js objects
  • Predictability and clarity of API - a small number of basic principles are reused in different cases, reducing the user's workload and increasing recognition. For example, if you know how .watch works for events, you already know how .watch works for stores.
  • The application is built from simple elements - space and way to take any required business logic out of the view, maximizing the simplicity of the components.

Installation

You can use any package manager

npm add effector

React

To getting started read our article how to write React and Typescript application.

npm add effector effector-react

SolidJS

npm add effector effector-solid

Vue

npm add effector effector-vue

Svelte

Svelte works with effector out of the box, no additional packages needed. See word chain game application written with svelte and effector.

CDN

Documentation

For additional information, guides and api reference visit our documentation site

Packages

Articles

Community

Online playground

You can try effector with online playground

Code sharing, Typescript and react supported out of the box. Playground repository

DevTools

Use effector-logger for printing updates to console, displaying current store values with ui or connecting application to familiar redux devtools


More examples in documentation

Learn more

Support us

Your support allows us to improve the developer experience 🧡.

Contributors

Dmitry/
Dmitry
andretshurotshka/
andretshurotshka
Sova/
Sova
Alexander
Alexander Khoroshikh
popuguy/
popuguy
Igor
Igor Kamyşev
Egor/
Egor
Valeriy
Valeriy Kobzar
Yan/
Yan
Ruslan
Ruslan @doasync
Illia
Illia Osmanov
mg901/
mg901
Igor
Igor Ryzhov
Arthur
Arthur Irgashev
Viktor/
Viktor
Ilya/
Ilya
Ainur/
Ainur
Arutiunian
Arutiunian Artem
Dmitrij
Dmitrij Shuleshov
Nikita
Nikita Nafranets
Ivan
Ivan Savichev
Aleksandr
Aleksandr Osipov
Зухриддин
Зухриддин Камильжанов
bakugod/
bakugod
Victor
Victor Didenko
Viktor
Viktor Pasynok
Kirill
Kirill Mironov
Andrei/
Andrei
Ivan/
Ivan
Bohdan
Bohdan Petrov
sergey20x25/
sergey20x25
Ivanov
Ivanov Vadim
Tauyekel
Tauyekel Kunzhol
Victor/
Victor
Vladimir
Vladimir Ivakin
Aldiyar
Aldiyar Batyrbekov
cqh/
cqh
xaota/
xaota
☃︎/
☃︎
Andrei
Andrei Antropov
Mikhail
Mikhail Kireev
Stanislav/
Stanislav
Sozonov/
Sozonov
Samir/
Samir
Renat
Renat Sagdeev
Kirill/
Kirill
Denis
Denis Sikuler
Arsen-95/
Arsen-95
Anton
Anton Yurovskykh
Aleksandr
Aleksandr Belov
Anton
Anton Kosykh
Usman
Usman Yunusov
vadimfilimonov/
vadimfilimonov
Vasili
Vasili Sviridov
Vasili
Vasili Svirydau
Victor
Victor Kolb
Vladislav
Vladislav Melnikov
Vladislav
Vladislav Botvin
The
The Gitter Badger
Shiyan7/
Shiyan7
Sergey
Sergey Belozyorcev
Satya
Satya Rohith
Roman/
Roman
Robert
Robert Kuzhin
Raman
Raman Aktsisiuk
Rachael
Rachael Dawn
Will
Will Heslam
Rasul
Rasul
Yesset
Yesset Zhussupov
Anatoly
Anatoly Kopyl
ansunrisein/
ansunrisein
Stanislav/
Stanislav
dmitryplyaskin/
dmitryplyaskin
Grigory
Grigory Zaripov
Houston
Houston (Bot)
ilfey/
ilfey
kanno/
kanno
Kirill
Kirill Leushkin
Marina
Marina Miyaoka
roman/
roman
vladthelittleone/
vladthelittleone
xxxxue/
xxxxue
0xflotus/
0xflotus
Abdukerim
Abdukerim Radjapov
7iomka/
7iomka
Abel
Abel Soares Siqueira
Aleksandr
Aleksandr Grigorii
Alex
Alex Arro
Aleksei
Aleksei Pudnikov
Alex
Alex Anokhin
Alexander/
Alexander
Александр/
Александр
Ayu/
Ayu
Dennis
Dennis Maush
Denis
Denis Skiba
Dmitry
Dmitry Dudin
Ed
Ed Prince
Gabriel
Gabriel Husek
Ilya
Ilya Martynov
Infant
Infant Frontender
Ivan/
Ivan
Jan
Jan Keromnes
Jesse
Jesse Jackson
Joel
Joel Bandi
Lebedev
Lebedev Konstantin
Leniorko/
Leniorko
Ludovic
Ludovic Dem
Nikita
Nikita Svoyachenko
Mike
Mike Cann
Oleg/
Oleg
Oleh/
Oleh
Pasha
Pasha Grekovich
bigslycat/
bigslycat

Tested with browserstack

patronum's People

Contributors

aanation avatar ainursharaev avatar alexandrhoroshih avatar binjospookie avatar blednaya-luna avatar chshanovskiy avatar dadayada avatar dependabot[bot] avatar doasync avatar egoson avatar evgenyifedotov avatar igorkamyshev avatar ilyaagarkov avatar iposokhin avatar kelin2025 avatar kireevmp avatar kobzarvs avatar laiff avatar meff34 avatar minhir avatar mitapfer avatar sergey20x25 avatar sergeysova avatar spoki4 avatar velialiev avatar victordidenko avatar yanlobat avatar yumauri avatar zarabotaet avatar zerobias 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

patronum's Issues

Add object form arguments for `delay`

const delayed = delay({
  source: unit,
  timeout: 100,
})

const delayed = delay({
  source: unit,
  timeout: (payload) => 100,
})

const delayed = delay({
  source: unit,
  timeout: $timeout,
})

Add object form arguments for `reshape`

Should support config normalization

const parts = reshape({
  source: $original,
  shape: {
    length: (string) => string.length,
    first: (string) => string.split(' ')[0] || '',
    second: (string) => string.split(' ')[1] || '',
  },
});

Refactor README

Maybe:

  • Add logo
  • Add description/summary for each method (in TOC or in each section?)
  • Operator decision tree

Add `lastTwo` method to save last two values from store

function lastTwo<T>(c: { source: Store<T>, target: Unit<{ current: T, prev: T }> }) {
  const store = createStore({ current: c.source.getState(), prev: c.source.getState() })

  store.on(c.source, ({ current: prev }, current) => ({ current, prev }))

  forward({ from: store, to: c.target })
}

const source = createStore(1)
const update = createEvent()

source.on(update, (e) => e + 1)

const target = createEvent<{ current: number, prev: number }>()

target.watch(console.info)

lastTwo({ source, target })

update()
update()
update()

https://share.effector.dev/DyKYa9Wc

[status]: `Fail` generic type

Hello! Currently status is passing Param & Done generics to underlying effect. All my effects have a custom 'Fail' type, so typescript complains about Types of property 'fail' are incompatible.. It would be nice to be able to pass Fail type

Condition types fail

const fxVoid = createEffect<void, void, void>();
const fxOtherVoid = createEffect<void, void, void>();
const someEvent = createEvent<void>();
const boolStore = createStore<boolean>(true);

condition({
    source: someEvent,
    if: boolStore,
    then: fxVoid,
    else: fxOtherVoid
})

This example gives me following error:

Argument of type '{ source: Event<void>; if: Store<boolean>; then: Effect<void, void, void>; else: Effect<void, void, void>; }' is not assignable to parameter of type '{ if: Store<boolean> | ((payload: Store<boolean>) => boolean); then: Unit<void | Store<boolean>>; else: Unit<void | Store<boolean>>; }'. Object literal may only specify known properties, and 'source' does not exist in type '{ if: Store<boolean> | ((payload: Store<boolean>) => boolean); then: Unit<void | Store<boolean>>; else: Unit<void | Store<boolean>>; }'. 

Implement build system

With the support of:

  • Tree shaking when imports from the index file
  • ESModules
  • CommonJS
  • Import from effector repl (AMD?)
  • Import specified method. Like import {} from 'patronum/reshape'

Add `borrow` method to create inheritor of effect

  • should infer types from original fx without changing params
import { createEffect, attach, Effect } from 'effector'

function borrow<P, D, F>(p: { effect: Effect<P, D, F> }): Effect<P, D, F> {
    return attach({
        effect: p.effect,
        mapParams: (params: P) => params,
    })
}

const parent = createEffect<number, string>()

const local = borrow({ effect: parent })

open in playground

Add object form arguments for `splitMap`

Should support config normalization

const received = splitMap({
  source: nameReceived,
  shape: {
    firstName: (string) => string.split(' ')[0], // string | undefined
    lastName: (string) => string.split(' ')[1], // string | undefined
  },
});

Add `postpone` method to delay triggering with abort support

postpone.ts
import { Effect, Event, Store, Unit, createEffect, forward } from 'effector';

type Node<T> = Event<T> | Store<T> | Effect<T, any, any>;

/**
 * Pause trigger from `source` on `delay` im ms before triggering `target`
 * It is like `forward`, but with delay
 */
export function postpone<T>(config: {
  source: Unit<T>;
  target: Unit<T>;
  delay: number;
  abort?: Node<any>;
}) {
  function abortable<P>(
    handler: (params: P) => Promise<P>,
    cancel?: Node<any>,
  ) {
    return (params: P) =>
      new Promise<P>((resolve, reject) => {
        const unsubscribe = cancel
          ? cancel.watch(() => {
              unsubscribe();
              reject(new Error('aborted'));
            })
          : () => {};

        handler(params)
          .then((done) => {
            unsubscribe();
            resolve(done);
          })
          .catch((error) => {
            unsubscribe();
            reject(error);
          });
      });
  }

  function timeout<P>(payload: P) {
    return new Promise<P>((resolve) =>
      setTimeout(resolve, config.delay, payload),
    );
  }

  const pauseFx = createEffect<T, T>();

  pauseFx.use(abortable(timeout, config.abort));

  forward({ from: config.source, to: pauseFx });
  forward({ from: pauseFx.doneData, to: config.target });
}
postpone.test.ts
import { allSettled, fork } from 'effector/fork';
import { createDomain } from 'effector';

import { postpone } from './postpone';

test('trigger target on source after delay', async () => {
  const app = createDomain();
  const source = app.createEvent<string>();
  const $target = app.createStore('');

  postpone({
    source,
    target: $target,
    delay: 10,
  });

  const scope = fork(app);
  const promise = allSettled(source, { scope, params: 'demo' });
  expect(scope.getState($target)).toBe('');

  await promise;
  expect(scope.getState($target)).toBe('demo');
});

test('abort triggering target', async () => {
  const app = createDomain();
  const source = app.createEvent<string>();
  const abort = app.createEvent();
  const $target = app.createStore('');

  postpone({
    source,
    target: $target,
    delay: 10,
    abort,
  });

  const scope = fork(app);
  const run = allSettled(source, { scope, params: 'demo' });
  expect(scope.getState($target)).toBe('');

  const cancel = allSettled(abort, { scope });

  await Promise.race([run, cancel]);
  expect(scope.getState($target)).toBe('');
});

test('abort correct scope', async () => {
  const app = createDomain();
  const source = app.createEvent<string>();
  const abort = app.createEvent();
  const $target = app.createStore('');

  postpone({
    source,
    target: $target,
    delay: 10,
    abort,
  });

  const scope1 = fork(app);
  const scope2 = fork(app);
  const run1 = allSettled(source, { scope: scope1, params: 'RUNNER 1' });
  const run2 = allSettled(source, { scope: scope2, params: 'SECOND' });

  expect(scope1.getState($target)).toBe('');
  expect(scope2.getState($target)).toBe('');

  const cancel1 = allSettled(abort, { scope: scope1 });

  await Promise.all([run1, run2, cancel1]);
  expect(scope1.getState($target)).toBe('');
  expect(scope2.getState($target)).toBe('SECOND');
});

Add `lastStates` method, that saves last states

const change = createEvent()
const $demo = restore(change, 0)
const $last = lastStates({ source: $demo, count: 3 })

$last.watch(console.info)

change(2)
change(50)
change(200)
change(5)

Console:

[0]
[2, 0]
[50, 2, 0]
[200, 50, 2]
[5, 200, 50]

Should be fixed:
https://share.effector.dev/HHVSrtqy


Option to prefill last states with initial state:

  • Can prefill have more readable name?
const change = createEvent()
const $demo = restore(change, 0)
const $last = lastStates({ source: $demo, count: 3, prefill: true })

$last.watch(console.info)

change(2)
change(50)
change(200)
change(5)

Console:

[0, 0, 0]
[2, 0, 0]
[50, 2, 0]
[200, 50, 2]
[5, 200, 50]

Option target to send last states to exist store:

  • Should it change .defaultState to prefilled, if prefill: true, or not?
  • If prefill: true update the target will trigger subscribers with new value, but what about initial trigger?
const app = createDomain()
const change = app.createEvent()
const $demo = restore(change, 0)

const $states = app.createStore([])
lastStates({ source: $demo, count: 3, target: $states })

$states.watch(console.info)

change(2)
change(50)
change(200)
change(5)

Console:

[0]
[2, 0]
[50, 2, 0]
[200, 50, 2]
[5, 200, 50]

`splitMap` do not updates second store

test('from readme', () => {
  const watchFirst = jest.fn();
  const watchLast = jest.fn();

  const nameReceived = createEvent<string>();
  const received = splitMap({
    source: nameReceived,
    cases: {
      firstName: (string) => string.split(' ')[0], // string | undefined
      lastName: (string) => string.split(' ')[1], // string | undefined
    },
  });

  received.firstName.watch(watchFirst);
  received.lastName.watch(watchLast);

  nameReceived('Sergey');
  expect(argumentHistory(watchFirst)).toMatchInlineSnapshot(`
    Array [
      "Sergey",
    ]
  `);
  expect(argumentHistory(watchLast)).toMatchInlineSnapshot(`Array []`);

  watchFirst.mockReset();
  watchFirst.mockReset();
  nameReceived('Sergey Sova');
  expect(argumentHistory(watchFirst)).toMatchInlineSnapshot(`
    Array [
      "Sergey",
    ]
  `);

  // This check fails, now watchLast do not triggered but must
  expect(argumentHistory(watchLast)).toMatchInlineSnapshot(`
    Array [
      "Sova",
    ]
  `);
});

Pending strategies

// no second argument anyOf set as default
const $processing = pending({ effects: [loadFirst, loadSecond] });

and as well I suppose to have a possibility to pick all strategy like

const $processing = pending({ effects: [loadFirst, loadSecond] }, 'all');

best case scenario when we would be able to merge several pending in one resulting one

did not make up API for that case yet

useCase like: effect1.pending && effect2.pending || effect3.pending || effec4.pending

Add `leading` and `trailing` for `throttle` like in lodash

Following this comment

To have more industry-like behaviour (lodash), throttle should provide options to indicate whether target should be triggered on the leading and/or trailing edge of the timeout.

leading [= true] (boolean): Specify triggering on the leading edge of the timeout
trailing [= true] (boolean): Specify triggering on the trailing edge of the timeout

If leading and trailing options are true, target is triggered on the trailing edge of the timeout only if the throttled source is triggered more than once during the timeout.

If timeout is 0 and leading is false, target triggering is deferred until to the next tick, similar to setTimeout with a timeout of 0.


const trigger = createEvent()

// by default `leading` is `true` and `trailing` is `true`
const throttled = throttle({ source: trigger, timeout: 100 })

trigger(1)

throttled should be triggered immediately, one time


const trigger = createEvent()

// by default `leading` is `true` and `trailing` is `true`
const throttled = throttle({ source: trigger, timeout: 100 })

trigger(1)
trigger(2)
trigger(3)

throttled should be triggered immediately with payload of 1 and second time after 100ms with payload of 3


const trigger = createEvent()
const throttled = throttle({ source: trigger, timeout: 100, leading: false })
trigger(1)

throttled should be triggered after 100ms, one time


const trigger = createEvent()
const throttled = throttle({ source: trigger, timeout: 100, leading: false })
trigger(1)
trigger(2)
trigger(3)

throttled should be triggered after 100ms, one time, with payload of 3 (just like current behaviour)


const trigger = createEvent()
const throttled = throttle({ source: trigger, timeout: 0, leading: false })
trigger(1)

throttled should be triggered on the next tick, one time (just like current behaviour)


const trigger = createEvent()
const throttled = throttle({ source: trigger, timeout: 100, trailing: false })

trigger(1)
await wait(75)
trigger(2)
await wait(75)
trigger(3)

throttled should be triggered immediately with payload of 1 and second time after 150ms with payload of 3


With combination leading: false, trailing: falsethrottled should not be triggered at all


Playground with lodash's throttle

Add support for `domain` in `pending`

import { createDomain } from 'effector';
import { pending } from 'patronum/pending';

const app = createDomain()
const loadFirst = app.createEffect().use(() => Promise.resolve(null));
const loadSecond = app.createEffect().use(() => Promise.resolve(2));
const $processing = pending({ domain: app });

$processing.watch((processing) => console.info(`processing: ${processing}`));
// => processing: false

loadFirst();
loadSecond();
// => processing: true

Typescript: Ability to assign Unit<void> for condition then, else

Current behavior

const fxVoid = createEffect<void, void, void();

condition({
  source: $isLive,
  if: Boolean,
  then: fxVoid, // this fails, since it expects Unit<boolean>
  else: fxVoid
})

Expected behavior

Not to fail when you supply Unit<void>

Solution

Change Unit<State> to Unit<State | void> in typings

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.