Published by sergeysova over 3 years ago
effector/babel-plugin
with factories
values
or handlers
arguments to fork
method, error will be thrown.Published by sergeysova over 3 years ago
useStoreMap
with scope (PR #474)Published by zerobias over 3 years ago
useStoreMap($store, value => result)
shorthandgetKey
to useList
. Function in this field will be used to compute key for every item of listupdateFilter
to useStoreMap
. It helps to control component rerendering, e.g. when component should rerender only when id
field is changeduseStore
(PR #431)createGate
to gate.displayName
and gate units (issue #449)useEvent
when used effect throw an errorPublished by zerobias over 3 years ago
sample({
clock: clickValidate,
source: $formFields,
target: [validateForm, sendStatsFx],
})
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,
}
})
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
})
sample
with clock
without source
. For example, it useful in cases when clock
is array of units and no source
stores is neededsample({
clock: [fx1.doneData, fx2.doneData],
fn: data => ({url: '/stats', data})
target: fetchFx,
})
clock
to guard
to improve developer expirience in cases when update trigger (clock
field) and data source (source
field) are different thingsguard({
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
Published by zerobias almost 4 years ago
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}
</>
)
}
Published by zerobias almost 4 years ago
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>
)
}
Published by zerobias almost 4 years ago
Published by zerobias almost 4 years ago
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
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
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
Published by zerobias about 4 years ago
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'}
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
Published by zerobias about 4 years ago
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'})
attach({source, effect})
without mapParams
: in case with source
and effect
only, inner effect will be triggered with source
valuesattach({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,
})
attach
mapParams
field: errors happened in mapParams
function will force attached effect to failsplit
and createApi
name
field to attach
typings.filter
and .filterMap
to effect typings (PR #376)forward
, attach
, sample
and guard
: attempt to call these methods without arguments will lead to error with user-friendly descriptionPublished by zerobias about 4 years ago
clock
field which acts like a merge
callimport {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,
})
sample
in case of config formpackage.json
to package exports
field (read more in nodejs documentation)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>
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()
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})
Published by zerobias over 4 years ago
Published by zerobias over 4 years ago
split
for pattern-matching without additional forwardsimport {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
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']
effector/fork
into effector
. Now all methods required for SSR are exported from the library itself, making effector/fork
an aliasScope
type alias for Fork
import {createStore} from 'effector/effector.mjs'
console.error
with undefined return, which was violating the type of effectrestore
aliases, event.filter(fn)
alias for event.filterMap(fn)
, greedy
in sample
as separate last argument and unused blocks
and Kind
Published by zerobias over 4 years ago
Gate.isOpen
plain property, which was incompatibile with concurrent mode and ssr, use Gate.status
insteadPublished by zerobias over 4 years ago
Published by zerobias over 4 years ago
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'))
domain
optional field to createGate
which will be used to create gate units (useful for ssr)createGate({domain}) in documentation
useList
hook typings for typescript exported from effector-react/ssr
by allowing usage as components' return value (fix DefinitelyTyped issue)Published by zerobias over 4 years ago
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
Published by zerobias over 4 years ago
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