Published by zerobias over 4 years ago
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]
createNode
to call without argumentsimport {createNode} from 'effector'
const node = createNode()
Step
type alias for Node
Published by zerobias over 4 years ago
Gate
state in createGate
via defaultState
fieldcreateGate({defaultState}) in documentation
object
restriction from createGate
Props
type in typescript, as it becomes useless with introduction of useGate
. This code now passes type checking successfullyimport {createGate} from 'effector-react'
const RouteGate = createGate<string>()
const UserGate = createGate({defaultState: 'guest'})
Published by zerobias over 4 years ago
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,
})
Published by zerobias over 4 years ago
Published by zerobias over 4 years ago
Vue.util.defineReactive
to Vue.observable
const fx = createEffect({...});
export default Vue.extend({
effector: {
isCompleted: fx.done
},
watch: {
isCompleted(sid) {
this.isPopupOpened = false;
}
},
data: () => ({
isPopupOpened: true,
})
})
const $msg = createStore();
export default Vue.extend({
effector: {
$msg
},
})
<template>
<input v-model="$msg">
</template>
Published by zerobias over 4 years ago
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']
scope.getState(store)
to access to store values in forked scopesvalues
support for fork
Published by zerobias over 4 years ago
effector-react/compat
and effector-vue/compat
compatibility with IE11Published by zerobias over 4 years ago
Vue.extend
(PR #343)Published by zerobias over 4 years ago
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
Published by zerobias over 4 years ago
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)
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
clearNode(unit)
from their parent domain hooks and history setsPublished by zerobias over 4 years ago
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>
)
Published by zerobias over 4 years ago
ignore
parameter to serialize
to skip stores during app state serialization (PR #325) (thanks @sergeysova)Published by zerobias over 4 years ago
useGate
, thereby making it consistent with <Gate />
createContextComponent
and createReactState
, which previously were based on createComponent
Published by zerobias over 4 years ago
effector/babel-plugin
when inability to determine unit name led to the absence of sid
propertyPublished by zerobias over 4 years ago
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],
})
hydrate
allSettled
support for effects as first argumentPublished by zerobias over 4 years ago
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
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'
})
*/
Published by zerobias over 4 years ago
ReadonlyArray
to useList
for typescriptPublished by zerobias over 4 years ago
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,
})
Published by zerobias over 4 years ago
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},
)
Published by zerobias over 4 years ago
domain.hooks
calls as escape hatch for imperative adding units to given domain