effector

Business logic with ease ☄️

MIT License

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

Published by sergeysova over 3 years ago

  • Fixed the conflict when used two versions of effector/babel-plugin with factories
  • If incorrect values passed to values or handlers arguments to fork method, error will be thrown.
effector - effector-react 21.3.1

Published by sergeysova over 3 years ago

  • Fixed TypeError in useStoreMap with scope (PR #474)
effector - effector-react 21.3.0

Published by zerobias over 3 years ago

  • Add useStoreMap($store, value => result) shorthand
  • Add support for getKey to useList. Function in this field will be used to compute key for every item of list
  • Add support for updateFilter to useStoreMap. It helps to control component rerendering, e.g. when component should rerender only when id field is changed
  • Add support for stores with functions in them to useStore (PR #431)
  • Add domain name passed to createGate to gate.displayName and gate units (issue #449)
  • Fix unhandled promise rejection in useEvent when used effect throw an error
effector - effector 21.8.0

Published by zerobias over 3 years ago

  • Add type support for arrays in sample target (PR #284, #445)
sample({
  clock: clickValidate,
  source: $formFields,
  target: [validateForm, sendStatsFx],
})
  • Add support for case functions, case stores and matcher stores to split. Case functions and stores will choose target case by its name, matcher stores are boolean stores which indicate whether to choose given case

const source = createEvent()
const even = createEvent()
const odd = createStore(0)

split({
  source,
  // matcher function
  match(n) {
    if (n % 2 === 0) return 'even'
    return 'odd'
  },
  cases: {
    even,
    odd,
  }
})

const $currentPage = createStore('dashboard')

split({
  source,
  // matcher store
  match: $currentPage,
  cases: {
    dashboard: even,
    __: odd,
  }
})

const tryUpdatePage = createEvent()
const updatePageFx = createEffect()



const $hasWriteAccess = createStore(false)


split({
  source: tryUpdatePage,
  match: {
    // case store
    admin: $hasWriteAccess
  },
  cases: {
    admin: updatePageFx,
  }
})

  • Add updateFilter config field to createStore to skip arbitrary store updates (discussion #428)
const $playerPosition = createStore({x: 0, y: 0}, {
  updateFilter: (update, current) => update.x !== current.x
})
  • Add support for sample with clock without source. For example, it useful in cases when clock is array of units and no source stores is needed
sample({
  clock: [fx1.doneData, fx2.doneData],
  fn: data => ({url: '/stats', data})
  target: fetchFx,
})
  • Add support for clock to guard to improve developer expirience in cases when update trigger (clock field) and data source (source field) are different things
guard({
  clock: validateForm,
  source: $formFields,
  filter: formFields => validator(formFields),
  target: submitFormFx,
})
  • Add addNames field to babel plugin (PR #450)

  • Add type support for Scope to clearNode (issue #441)

  • Add compositeName to Domain typings, making it consistent with other units

  • Add EventPayload and UnitValue type helpers (PR #434)

  • Improve various edge cases with fork api and serialization

  • Improve typechecking for attach (issue #439)

  • Fix various type issues in sample and guard typings

effector - effector-react 21.2.0

Published by zerobias almost 4 years ago

  • Add createGate implementation to effector-react/ssr
import {createDomain} from 'effector'
import {createGate, useGate} from 'effector-react/ssr'

const app = createDomain()

const currentRouteGate = createGate({
  domain: app,
  defaultState: 'dashboard',
})

export const Layout = ({routeName, children}) => {
  useGate(currentRouteGate, routeName)
  return (
    <>
      <h1>{routeName}</h1>
      {children}
    </>
  )
}
effector - effector-react 21.1.0

Published by zerobias almost 4 years ago

  • Add support for object and array of events to useEvent. It's a shorthand for calling several useEvent at once (PR #425 by @sergeysova)
export function ExampleComponent() {
  const handlers = useEvent({emailChanged, passwordChanged})

  return (
    <div>
      <input onChange={handlers.emailChanged} />
      <input onChange={handlers.passwordChanged} />
    </div>
  )
}
export function ExampleComponent() {
  const [changeEmail, changePassword] = useEvent([
    emailChanged,
    passwordChanged,
  ])

  return (
    <div>
      <input onChange={changeEmail} />
      <input onChange={changePassword} />
    </div>
  )
}
effector - effector 21.7.0

Published by zerobias almost 4 years ago

  • Add support for scopes to hydrate, to provide a way to fill additional values to existing scope (happens during SSG navigation in next.js)

  • Improve prepend type inference (PR #415 (thanks @doasync))

effector - effector 21.6.0

Published by zerobias almost 4 years ago

  • Add support for user-defined factories in fork api. Starting from this release, application developers can use their own functions and be sure that their content will be properly serialized and hydrated by fork api.
    New field factories in effector/babel-plugin accepts array of module names which exports will be treated as custom factories therefore each function call will provide unique prefix for sid properties of units inside them
{
  "plugins": [
    [
      "effector/babel-plugin",
      {
        "factories": ["src/createEffectStatus", "~/createCommonPending"]
      }
    ]
  ]
}
import {rootDomain} from './rootDomain'

export function createEffectStatus(fx) {
  const $status = rootDomain
    .createStore('init')
    .on(fx.finally, (_, {status}) => status)
  return $status
}
import {createEffectStatus} from './createEffectStatus'
import {fetchUserFx, fetchFriendsFx} from './api'

export const $fetchUserStatus = createEffectStatus(fetchUserFx)
export const $fetchFriendsStatus = createEffectStatus(fetchFriendsFx)

Import createEffectStatus from './createEffectStatus' was treated as factory function so each store created by it has its own sid and will be handled by serialize independently, although without factories they will share the same sid

factories in documentation

  • Add user-friendly unit name in fork api error messages when given unit is not found in scope. This improves error messages in both effector and effector-react

  • Add validation for values and handlers fields in fork and for values field in hydrate

  • Add type support for createEffect<typeof handler, Error>(handler) to infer Params and Done types from given handler and provide custom Fail type at the same time

createEffect and custom errors in documentation

  • Improve guard return type inference (PR #406 (thanks @doasync))

  • Fix void params support for createEffect(name, config) (issue #404)

  • Allow to use Event<void> in cases when only () => void is accepted (PR #400 (thanks @doasync))

  • Add support for merge to effector/babel-plugin

effector - effector 21.5.0

Published by zerobias about 4 years ago

  • Add support for attach({effect}) to create effect which will call effect with params as it is. That allow to create separate effects with shared behavior (PR #396 and #397 (thanks @sergeysova and @oas89))

  • Add reactSsr option to effector/babel-plugin to replace imports from effector-react to effector-react/ssr. Useful for building both server-side and client-side builds from the same codebase

effector - effector 21.4.0

Published by zerobias about 4 years ago

  • Add support for return status to allSettled. When allSettled is called with Effect, it return object with value and status fields (discussion)
import {createDomain, fork, allSettled} from 'effector'
const app = createDomain()
const fx = app.createEffect(() => 'ok')
const result = await allSettled(fx, {scope: fork(app)})
// => {status: 'done', value: 'ok'}

Try it

  • Allow to expicitly define return/error types in createEffect(handler)
const fx = createEffect<number, string, Error>(x => x.toString())
// fx has type Effect<number, string, Error>
  • Add types for domain.effect(handler)

  • Fix effector/babel-plugin behavior in case when methods like createStore are imported from unrelated library and should be ignored. Import library could be defined by importName config field

  • Improve fork api support for watchers

effector - effector 21.3.0

Published by zerobias about 4 years ago

  • Add support for createEffect(handler)

createEffect(handler) in documentation

import {createEffect} from 'effector'

const fetchUserReposFx = createEffect(async ({name}) => {
  const url = `https://api.github.com/users/${name}/repos`
  const req = await fetch(url)
  return req.json()
})

fetchUserReposFx.done.watch(({params, result}) => {
  console.log(result)
})

await fetchUserReposFx({name: 'zerobias'})

Try it

  • Add support for attach({source, effect}) without mapParams: in case with source and effect only, inner effect will be triggered with source values

attach({effect, source}) in documentation

import {createStore, createEffect, attach} from 'effector'
const request = createEffect()
const token = createStore('')
const secureRequest = attach({effect: request, source: token})

it's a shorthand for common use case:

import {createStore, createEffect, attach} from 'effector'
const request = createEffect()
const token = createStore('')
const secureRequest = attach({
  effect: request,
  source: token,
  mapParams: (_, source) => source,
})

Try it

  • Handle throws in attach mapParams field: errors happened in mapParams function will force attached effect to fail
  • Add babel plugin support for split and createApi
  • Add name field to attach typings
  • Add .filter and .filterMap to effect typings (PR #376)
  • Improve config validation for forward, attach, sample and guard: attempt to call these methods without arguments will lead to error with user-friendly description
  • Improve fork api support for stores and events
effector - effector 21.2.0

Published by zerobias about 4 years ago

  • Add array support for sample clock field which acts like a merge call
import {createStore, createEvent, createEffect, sample, merge} from 'effector'

const showNotification = createEvent<string>()
const trigger = createEvent()
const fx = createEffect()
const store = createStore('')

// array of units in clock
sample({
  source: store,
  clock: [trigger, fx.doneData],
  target: showNotification,
})

// merged unit in clock
sample({
  source: store,
  clock: merge([trigger, fx.doneData]),
  target: showNotification,
})
  • Improve ide type hints support for sample in case of config form
  • Add package.json to package exports field (read more in nodejs documentation)
effector - effector 21.1.0

Published by zerobias about 4 years ago

  • Add onlyChanges option to serialize to ignore stores which didn't changed in fork (prevent default values from being carried over network)

  • Add type helpers for stores and effects: StoreValue, EffectParams, EffectResult and EffectError

import {
  createStore,
  createEffect,
  StoreValue,
  EffectParams,
  EffectResult,
} from 'effector'

const username = createStore('guest')

const getUserFX = createEffect<number, {name: string}>()

// string
type Username = StoreValue<typeof username>

// number
type GetUserParams = EffectParams<typeof getUserFX>

// {name: string}
type User = EffectResult<typeof getUserFX>
  • Allow domain.createEffect to infer type from given handler (that feature was already implemented for createEffect method), this code now typechecked as expected:
import {createDomain} from 'effector'

const app = createDomain()

const voidFx = app.createEffect({
  async handler() {},
})

await voidFx()
  • Allow to call allSettled with void units without params field, this code now typechecked as expected:
import {createDomain, fork, allSettled} from 'effector'

const app = createDomain()

const voidFx = app.createEffect({
  async handler() {},
})

voidFx()

const scope = fork(app)

await allSettled(voidFx, {scope})
effector - effector 21.0.3, effector-react 21.0.4, effector-vue 21.0.3

Published by zerobias over 4 years ago

effector - effector 21.0.0

Published by zerobias over 4 years ago

  • Add object form of split for pattern-matching without additional forwards

split in documentation

import {split, createEffect, createEvent} from 'effector'
const messageReceived = createEvent()
const showTextPopup = createEvent()
const playAudio = createEvent()
const reportUnknownMessageType = createEffect({
  handler({type}) {
    console.log('unknown message:', type)
  },
})

split({
  source: messageReceived,
  match: {
    text: msg => msg.type === 'text',
    audio: msg => msg.type === 'audio',
  },
  cases: {
    text: showTextPopup,
    audio: playAudio,
    __: reportUnknownMessageType,
  },
})

showTextPopup.watch(({value}) => {
  console.log('new message:', value)
})

messageReceived({
  type: 'text',
  value: 'Hello',
})
// => new message: Hello
messageReceived({
  type: 'image',
  imageUrl: '...',
})
// => unknown message: image

Try it

You can match directly to store api as well:

import {split, createStore, createEvent, createApi} from 'effector'

const textContent = createStore([])

const messageReceived = createEvent()

split({
  source: messageReceived,
  match: {
    text: msg => msg.type === 'text',
    audio: msg => msg.type === 'audio',
  },
  cases: createApi(textContent, {
    text: (list, {value}) => [...list, value],
    audio: (list, {duration}) => [...list, `audio ${duration} ms`],
    __: list => [...list, 'unknown message'],
  }),
})

textContent.watch(messages => {
  console.log(messages)
})

messageReceived({
  type: 'text',
  value: 'Hello',
})
// => ['Hello']
messageReceived({
  type: 'image',
  imageUrl: '...',
})
// => ['Hello', 'unknown message']
messageReceived({
  type: 'audio',
  duration: 500,
})
// => ['Hello', 'unknown message', 'audio 500 ms']

Try it

  • Merge effector/fork into effector. Now all methods required for SSR are exported from the library itself, making effector/fork an alias
  • Make Scope type alias for Fork
  • Add support for es modules: import {createStore} from 'effector/effector.mjs'
  • Effect without a handler now throws an error during a call instead of calling console.error with undefined return, which was violating the type of effect
  • Remove restore aliases, event.filter(fn) alias for event.filterMap(fn), greedy in sample as separate last argument and unused blocks and Kind
effector - effector-react 21.0.0

Published by zerobias over 4 years ago

  • Add support for es modules
  • Remove experimental Gate.isOpen plain property, which was incompatibile with concurrent mode and ssr, use Gate.status instead

Gate.status in documentation

effector - effector-vue 21.0.0

Published by zerobias over 4 years ago

  • Add support for es modules
effector - effector-react 20.9.0

Published by zerobias over 4 years ago

  • Export useGate with fork support from effector-react/ssr
import {useGate, useStore, Provider} from 'effector-react/ssr'
import {createGate} from 'effector-react'
import {createDomain, forward} from 'effector'
import {fork} from 'effector/fork'

const app = createDomain()

const activeChatGate = createGate({domain: app})

const getMessagesFx = app.createEffect({
  async handler({chatId}) {
    return ['hi bob!', 'Hello, Alice']
  },
})
const messagesAmount = app
  .createStore(0)
  .on(getMessagesFx.doneData, (_, messages) => messages.length)

forward({
  from: activeChatGate.open,
  to: getMessagesFx,
})

const ChatPage = ({chatId}) => {
  useGate(activeChatGate, {chatId})
  return (
    <div>
      <header>Chat {chatId}</header>
      <p>Messages total: {useStore(messagesAmount)}</p>
    </div>
  )
}
const App = ({root}) => (
  <Provider value={root}>
    <ChatPage chatId="chat01" />
  </Provider>
)

const scope = fork(app)

ReactDOM.render(<App root={scope} />, document.getElementById('root'))

Try it

  • Add domain optional field to createGate which will be used to create gate units (useful for ssr)

createGate({domain}) in documentation

  • Improve useList hook typings for typescript exported from effector-react/ssr by allowing usage as components' return value (fix DefinitelyTyped issue)
effector - effector 20.17.2

Published by zerobias over 4 years ago

  • Add effects created via attach to domain effects, allowing these effects to be called within other effects when using fork
import {createDomain, attach} from 'effector'
import {fork, allSettled} from 'effector/fork'

const app = createDomain()

const add = app.createEffect({handler: _ => _})

const count = app.createStore(2).on(add.doneData, (x, y) => x + y)

const addWithCurrent = attach({
  source: count,
  effect: add,
  mapParams: (params, current) => params + current,
})

const start = app.createEffect({
  async handler(val) {
    await addWithCurrent(val)
  },
})

const scope = fork(app)

await allSettled(start, {
  scope,
  params: 3,
})

console.log(scope.getState(count))
// => 7

Try it

  • Add validation for combine first argument which should be store, object with stores or array with stores PR #362 (thanks @doasync)
effector - effector 20.17.1

Published by zerobias over 4 years ago

  • Add validation for event.watch watcher, this code now throw error as expected:
import {createEvent} from 'effector'

const trigger = createEvent()

trigger.watch(NaN)
// => Error: .watch argument should be a function

Try it

Package Rankings
Top 1.08% on Npmjs.org
Top 14.96% on Deno.land
Badges
Extracted from project README
Tested with browserstack