effector

Business logic with ease ☄️

MIT License

Downloads
370.3K
Stars
4.5K
Committers
151
effector - effector 20.17.0

Published by zerobias over 4 years ago

  • Add support for nested effect calls in forked scope. Parallel requests are supported as well
import {createDomain, forward} from 'effector'
import {fork, allSettled} from 'effector/fork'

const app = createDomain()

const addWord = app.createEffect({handler: async word => word})
const words = app
  .createStore([])
  .on(addWord.doneData, (list, word) => [...list, word])

const start = app.createEffect({
  async handler(word) {
    await addWord(`${word}1`)
    await addWord(`${word}2`)
    return word
  },
})

const next = app.createEffect({
  async handler(word) {
    await Promise.all([addWord(`${word}3`), addWord(`${word}4`)])
  },
})

forward({from: start.doneData, to: next})

const scopeA = fork(app)
const scopeB = fork(app)
const scopeC = fork(app)

await Promise.all([
  allSettled(start, {
    scope: scopeA,
    params: 'A',
  }),
  allSettled(start, {
    scope: scopeB,
    params: 'B',
  }),
])

await allSettled(start, {
  scope: scopeC,
  params: 'C',
})

console.log(scopeA.getState(words))
// => [A1, A2, A3, A4]
console.log(scopeB.getState(words))
// => [B1, B2, B3, B4]
console.log(scopeC.getState(words))
// => [C1, C2, C3, C4]

Try it

  • Allow createNode to call without arguments
import {createNode} from 'effector'

const node = createNode()
  • Make Step type alias for Node
effector - effector-react 20.8.0

Published by zerobias over 4 years ago

  • Add ability to define default Gate state in createGate via defaultState field

createGate({defaultState}) in documentation

  • Remove object restriction from createGate Props type in typescript, as it becomes useless with introduction of useGate. This code now passes type checking successfully
import {createGate} from 'effector-react'

const RouteGate = createGate<string>()

const UserGate = createGate({defaultState: 'guest'})
effector - effector 20.16.1

Published by zerobias over 4 years ago

  • Allow typescript to refine type if guard got Boolean (a function) as filter
import {createEvent, createStore, guard} from 'effector'

type User = {name: string}

const trigger = createEvent<User | null>()
const user = createStore<User>({name: 'alice'})

guard({
  source: trigger,
  filter: Boolean,
  target: user,
})
effector - effector-react 20.7.4

Published by zerobias over 4 years ago

effector - effector-vue 20.5.0

Published by zerobias over 4 years ago

  • Migrated from Vue.util.defineReactive to Vue.observable
  • Effector stores will show in Vue devtools
  • Cosmetic improvements for support plugin in the future
  • Now we can add some units to effector object (will be return Store)
const fx = createEffect({...});
export default Vue.extend({
  effector: {
    isCompleted: fx.done
  },
  watch: {
    isCompleted(sid) {
      this.isPopupOpened = false;
    }
  },
  data: () => ({
    isPopupOpened: true,
  })
})
  • Support v-model directive for scalar values
const $msg = createStore();
export default Vue.extend({
  effector: {
    $msg
  },
})
<template>
  <input v-model="$msg">
</template>
effector - effector 20.16.0

Published by zerobias over 4 years ago

  • Add support for handlers to fork to change effect handlers for forked scope (useful for testing)
import {createDomain} from 'effector'
import {fork, allSettled} from 'effector/fork'

//app
const app = createDomain()
const fetchFriends = app.createEffect<{limit: number}, string[]>({
  async handler({limit}) {
    /* some client-side data fetching */
    return []
  },
})
const user = app.createStore('guest')
const friends = app
  .createStore([])
  .on(fetchFriends.doneData, (_, result) => result)

/*
  test to ensure that friends value is populated
  after fetchFriends call
*/
const testScope = fork(app, {
  values: {
    [user.sid]: 'alice',
  },
  handlers: {
    [fetchFriends.sid]: () => ['bob', 'carol'],
  },
})

/* trigger computations in scope and await all called effects */
await allSettled(fetchFriends, {
  scope: testScope,
  params: {limit: 10},
})

/* check value of store in scope */
console.log(testScope.getState(friends))
// => ['bob', 'carol']

Try it

  • Add support for scope.getState(store) to access to store values in forked scopes
  • Fix values support for fork
effector - effector-react 20.7.3, effector-vue 20.4.2

Published by zerobias over 4 years ago

  • Fix regression in effector-react/compat and effector-vue/compat compatibility with IE11
effector - effector-vue 20.4.1

Published by zerobias over 4 years ago

  • Improve typescript typings for usage via Vue.extend (PR #343)
effector - effector 20.15.1

Published by zerobias over 4 years ago

  • Fix additional store updates during state hydration
import {createDomain, forward} from 'effector'
import {hydrate} from 'effector/fork'

const app = createDomain()

const username = app.createStore('guest')
const saveUser = app.createEffect({
  handler(value) {
    console.log('saveUser now called only after store update', value)
  },
})

forward({
  from: username,
  to: saveUser,
})

username.updates.watch(value => {
  console.log('event watches now called only after store update', value)
})

hydrate(app, {
  values: {
    [username.sid]: 'alice',
  },
})
// no event watches triggered yet and no effects called as we just hydrating app state

Try it

effector - effector 20.15.0

Published by zerobias over 4 years ago

  • Add support for array of units to store.on (PR #328)
import {createEvent, createStore} from 'effector'

const store = createStore(0)
const changedA = createEvent()
const changedB = createEvent()

store.on([changedA, changedB], (state, params) => state + params)

store.watch(value => {
  console.log('updated', value)
})

changedA(2)
// => updated 2

changedB(2)
// => updated 4

// You can unsubscribe from any trigger
store.off(changedA)

Try it

Documentation for store.on

  • Add support for array of units to store.reset to make it consistent with merge and store.on
import {createEvent, createStore} from 'effector'

const store = createStore(0)
const increment = createEvent()
const reset = createEvent()

store.on(increment, state => state + 1).reset([reset])

store.watch(state => console.log('changed', state))
// changed 0
// watch method calls its function immediately

increment() // changed 1
increment() // changed 2
reset() // changed 0

Try it

Documentation for store.reset

  • Remove units erased with clearNode(unit) from their parent domain hooks and history sets
effector - effector-react 20.7.1

Published by zerobias over 4 years ago

  • Improve useList hook typings for typescript by allowing usage as components' return value (fix DefinitelyTyped issue)

This code now works without type errors:

import {createStore} from 'effector'
import {useList} from 'effector-react'

const users = createStore<User[]>([
  {
    username: 'alice',
    email: '[email protected]',
    bio: '. . .',
  },
  {
    username: 'bob',
    email: '[email protected]',
    bio: '~/ - /~',
  },
  {
    username: 'carol',
    email: '[email protected]',
    bio: '- - -',
  },
])
const UserList = () => useList(users, ({username}) => <p>{username}</p>)
const App = () => (
  <div>
    <UserList />
  </div>
)
effector - effector 20.14.0

Published by zerobias over 4 years ago

  • Add ignore parameter to serialize to skip stores during app state serialization (PR #325) (thanks @sergeysova)
effector - effector-react 20.7.0

Published by zerobias over 4 years ago

  • Use shallow compare for skipping updates with useGate, thereby making it consistent with <Gate />
  • Remove nesting from components, created by createContextComponent and createReactState, which previously were based on createComponent
effector - effector 20.13.6

Published by zerobias over 4 years ago

  • Fix cases with effector/babel-plugin when inability to determine unit name led to the absence of sid property
effector - effector 20.13.5

Published by zerobias over 4 years ago

  • Extend typescript support for any to void forwarding: add support for forwarding to array of void units
import {forward, createEvent, createEffect} from 'effector'

const sourceA = createEvent<string>()
const sourceB = createEvent<number>()

const targetA = createEvent<void>()
const targetB = createEffect<void, any>()

forward({
  from: sourceA,
  to: targetA,
})

forward({
  from: sourceA,
  to: [targetA, targetB],
})

forward({
  from: [sourceA, sourceB],
  to: targetA,
})

forward({
  from: [sourceA, sourceB],
  to: [targetA, targetB],
})
  • Fix computed stores support for hydrate
  • Fix allSettled support for effects as first argument
effector - effector 20.13.0

Published by zerobias over 4 years ago

  • Introduce attach: wrapper for effect, which allow to map effect arguments and use data from stores.

Use cases: declarative passing values from stores to effects and argument preprocessing.

import {createEffect, attach, createStore} from 'effector'

const backendRequest = createEffect({
  async handler({token, data, resource}) {
    const req = fetch(`https://example.com/api${resource}`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(data),
    })
  },
})

const requestsSend = createStore(0).on(backendRequest, total => total + 1)

requestsSend.watch(total => {
  console.log(`client analytics: sent ${total} requests`)
})

const token = createStore('guest_token')

const authorizedRequest = attach({
  effect: backendRequest,
  source: token,
  mapParams: ({data, resource}, token) => ({data, resource, token}),
})

const createRequest = resource =>
  attach({
    effect: authorizedRequest,
    mapParams: data => ({data, resource}),
  })

const getUser = createRequest('/user')
const getPosts = createRequest('/posts')

const user = await getUser({name: 'alice'})
/*
POST https://example.com/api/user
{"name": "alice"}
Authorization: Bearer guest_token
*/

// => client analytics: sent 1 requests

const posts = await getPosts({user: user.id})
/*
POST https://example.com/api/posts
{"user": 18329}
Authorization: Bearer guest_token
*/

// => client analytics: sent 2 requests

Documentation for attach

  • Add noDefaults option for effector/babel-plugin for making custom unit fabrics with clean configuration
{
  "plugins": [
    ["effector/babel-plugin", {"addLoc": true}],
    [
      "effector/babel-plugin",
      {
        "importName": "@lib/createInputField",
        "storeCreators": ["createInputField"],
        "noDefaults": true
      },
      "createInputField"
    ]
  ]
}
// @lib/createInputField.js
import {createStore} from 'effector'
import {resetForm} from './form'

export function createInputField(defaultState, {sid, name}) {
  return createStore(defaultState, {sid, name}).reset(resetForm)
}

// src/state.js
import {createInputField} from '@lib/createInputField'

const foo = createInputField('-')
/* 

will be treated as store creator and compiled to

const foo = createInputField('-', {
  name: 'foo',
  sid: 'z&si65'
})

*/
effector - effector-react 20.6.3

Published by zerobias over 4 years ago

  • Add type support for stores with ReadonlyArray to useList for typescript
effector - effector 20.12.2

Published by zerobias over 4 years ago

  • Add type support for sample with target and without clock (in that case, source will become clock as well)
import {createStore, sample} from 'effector'

const source = createStore([{foo: 0}])
const target = createStore(0)

sample({
  source,
  target,
  fn: list => list.length,
})

Try it

effector - effector-vue 20.4.0

Published by zerobias over 4 years ago

  • Add support for vue component watch option (PR #313) (thanks @Fl0pZz)
import {createStore} from 'effector'
import {createComponent} from 'effector-vue'

const counter = createStore(0)

const component = createComponent(
  {
    template: '<div>{{ counter }}</div>',
    watch: {
      counter() {
        /* side-effects here */
      },
    },
  },
  {counter},
)
effector - effector 20.12.1

Published by zerobias over 4 years ago

  • Add support for guard to babel-plugin
  • Add support for forward to babel-plugin
  • Add support for explicit domain.hooks calls as escape hatch for imperative adding units to given domain
Package Rankings
Top 1.08% on Npmjs.org
Top 14.96% on Deno.land
Badges
Extracted from project README
Tested with browserstack