Published by zerobias about 5 years ago
clearNode
been called on store belonged to certain domain led to the removal of the entire domainPublished by zerobias about 5 years ago
keys
field in useList
. By default, useList
rerenders only when some of its items were changed.import React from 'react'
import ReactDOM from 'react-dom'
import {createEvent, createStore, restore} from 'effector'
import {useStore, useList} from 'effector-react'
const renameUser = createEvent()
const user = restore(renameUser, 'alice')
const friends = createStore(['bob'])
const List = () => {
const userName = useStore(user)
return useList(friends, {
keys: [userName],
fn: friend => (
<div>
{friend} is a friend of {userName}
</div>
),
})
}
ReactDOM.render(<List />, document.getElementById('root'))
// => <div> bob is a friend of alice </div>
setTimeout(() => {
renameUser('carol')
// => <div> bob is a friend of carol </div>
}, 500)
Published by zerobias about 5 years ago
shortName
to domainsimport {createDomain} from 'effector'
const domain = createDomain('feature')
console.log(domain.shortName)
// => feature
history
to domains with read-only sets of events, effects, stores and subdomainsimport {createDomain} from 'effector'
const domain = createDomain()
const eventA = domain.event()
const storeB = domain.store(0)
console.log(domain.history)
// => {stores: Set{storeB}, events: Set{eventA}, domains: Set, effects: Set}
Published by goodmind about 5 years ago
const counter = createStore(0)
new Vue({
effector: {
counter, // would create `counter` in template
}
})
Published by sergeysova about 5 years ago
Add .sid
— stable hash identifier for events, effects, stores and domains, preserved between environments, to handle client-server interaction within the same codebase
See example project
Published by zerobias about 5 years ago
clearNode
import {createDomain, clearNode} from 'effector'
const root = createDomain()
const child = root.domain()
clearNode(child)
.sid
- stable hash identifier for events, effects, stores and domains, preserved between environments, to handle client-server interaction within the same codebase./* common.js */
import {createEffect} from 'effector'
export const getUser = createEffect({sid: 'GET /user'})
console.log(getUsers.sid)
// => GET /user
/* worker.js */
import {getUsers} from './common'
getUsers.use(userID => fetch(userID))
getUsers.done.watch(({result}) => {
postMessage({sid: getUsers.sid, result})
})
onmessage = async ({data}) => {
if (data.sid !== getUsers.sid) return
getUsers(data.userID)
}
/* client.js */
import {createEvent} from 'effector'
import {getUsers} from './common'
const onMessage = createEvent()
const worker = new Worker('worker.js')
worker.onmessage = onMessage
getUsers.use(
userID =>
new Promise(rs => {
worker.postMessage({sid: getUsers.sid, userID})
const unwatch = onMessage.watch(({data}) => {
if (data.sid !== getUsers.sid) return
unwatch()
rs(data.result)
})
}),
)
The key is that sid can be autogenerated by effector/babel-plugin
with default config and it will be stable between builds
See example project
createEffect
for typescript #106
const handler = () => console.log()
const effect = createEffect({handler})
effect()
cannot read property .toString of undefined
error during store initializationPublished by zerobias about 5 years ago
createComponent
Published by zerobias about 5 years ago
event.filter({fn})
got a predicate function as a callback PR
import {createEvent} from 'effector'
const event = createEvent<string | number>()
const isString = (value: any): value is string => typeof value === 'string'
const isNumber = (value: any): value is number => typeof value === 'number'
const str = event.filter({fn: isString}) // Event<string>
const num = event.filter({fn: isNumber}) // Event<number>
str.watch(value => value.slice()) // OK now
num.watch(value => value.toFixed(2)) // OK now
is
methods PR
import {is} from 'effector'
//result has type Event<any> | void
function getEvent(obj: unknown) {
if (is.event(obj)) return obj
if (is.store(obj)) return obj.updates
}
Published by zerobias about 5 years ago
effector/compat
sample
effector/babel-plugin
to work in browsereffector-react/compat
and effector-vue/compat
Published by zerobias about 5 years ago
effector-react/compat
module to use with Smart TV (Chrome 47) apps without babelPublished by zerobias about 5 years ago
effector/compat
module to use with Smart TV (Chrome 47) apps without babel (fix #152). Starting with this release, the library code is tested by browserstack.com for compatibility with our targets, including smart tvsample
(thanks @abliarsar) (PR #156)Published by zerobias over 5 years ago
useList
for efficient rendering of store listsimport React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'effector'
import {useList} from 'effector-react'
const list = createStore([
{name: 'alice', age: 21},
{name: 'bob', age: 20},
{name: 'carol', age: 22},
])
const List = () => {
// note that we don't need keys here any more
const users = useList(list, ({name}, i) => (
<div>
{i}) {name}
</div>
))
return <div>{users}</div>
}
ReactDOM.render(<List />, document.getElementById('root'))
Published by zerobias over 5 years ago
as const
typescript assertion for useStoreMap
keys. It helps us to infer type for fn
argumentsimport React from 'react'
import {createStore} from 'effector'
import {useStoreMap} from 'effector-react'
type User = {
username: string
email: string
bio: string
}
const users = createStore<User[]>([
{
username: 'alice',
email: '[email protected]',
bio: '. . .',
},
{
username: 'bob',
email: '[email protected]',
bio: '~/ - /~',
},
{
username: 'carol',
email: '[email protected]',
bio: '- - -',
},
])
export const UserProperty = ({id, field}: {id: number; field: keyof User}) => {
const value = useStoreMap({
store: users,
keys: [id, field] as const,
fn: (users, [id, field]) => users[id][field] || null,
})
return <div>{value}</div>
}
In typescript versions below 3.4, you can still use an explicit type assertion
import React from 'react'
import {createStore} from 'effector'
import {useStoreMap} from 'effector-react'
type User = {
username: string
email: string
bio: string
}
const users = createStore<User[]>([
{
username: 'alice',
email: '[email protected]',
bio: '. . .',
},
{
username: 'bob',
email: '[email protected]',
bio: '~/ - /~',
},
{
username: 'carol',
email: '[email protected]',
bio: '- - -',
},
])
export const UserProperty = ({id, field}: {id: number; field: keyof User}) => {
const value = useStoreMap({
store: users,
keys: [id, field] as [number, keyof User],
fn: (users, [id, field]) => users[id][field] || null,
})
return <div>{value}</div>
}
as const in typescript docs
Published by zerobias over 5 years ago
useStore
argument changePublished by zerobias over 5 years ago
useStoreMap
Published by zerobias over 5 years ago
merge
for merging eventsimport {createEvent, merge} from 'effector'
const foo = createEvent()
const bar = createEvent()
const baz = merge([foo, bar])
baz.watch(v => console.log('merged event triggered: ', v))
foo(1)
// => merged event triggered: 1
bar(2)
// => merged event triggered: 2
split
for pattern-matching over eventsimport {createEvent, split} from 'effector'
const message = createEvent()
const messageByAuthor = split(message, {
bob: ({user}) => user === 'bob',
alice: ({user}) => user === 'alice',
})
messageByAuthor.bob.watch(({text}) => {
console.log('[bob]: ', text)
})
messageByAuthor.alice.watch(({text}) => {
console.log('[alice]: ', text)
})
message({user: 'bob', text: 'Hello'})
// => [bob]: Hello
message({user: 'alice', text: 'Hi bob'})
// => [alice]: Hi bob
/* default case, triggered if no one condition met */
const {__: guest} = messageByAuthor
guest.watch(({text}) => {
console.log('[guest]: ', text)
})
message({user: 'unregistered', text: 'hi'})
// => [guest]: hi
clearNode
to automatically dispose all related intermediate stepsimport {createEvent, clearNode} from 'effector'
const source = createEvent()
const target = source.map(x => {
console.log('intermediate step')
return x
})
target.watch(x => console.log('target watcher'))
source()
// => intermediate step
// => target watcher
clearNode(target)
source() // ~ no reaction ~
Fix promise warning for effects
Add effect.finally
import {createEffect} from 'effector'
const fetchApi = createEffect({
handler: n =>
new Promise(resolve => {
setTimeout(resolve, n, `${n} ms`)
}),
})
fetchApi.finally.watch(response => {
console.log(response)
})
await fetchApi(10)
// => {status: 'done', result: '10 ms', params: 10}
// or
// => {status: 'fail', error: Error, params: 10}
event.filterMap
as new alias for event.filter(fn)
extract
, withProps
, is.*
reexportsPublished by zerobias over 5 years ago
unstable_createStoreProvider
Published by zerobias over 5 years ago
Vue adapter for effector 20
Published by zerobias over 5 years ago
useStoreMap
hook to select part from a store. Motivation
import {createStore} from 'effector'
import {useStore, useStoreMap} from 'effector-react'
import React from 'react'
import ReactDOM from 'react-dom'
const User = ({id}) => {
const user = useStoreMap({
store: user$,
keys: [id],
fn: (users, [id]) => users[id],
})
return (
<div>
{user.name} ({user.age})
</div>
)
}
const UserList = () => useStore(userID$).map(id => <User id={id} key={id} />)
const user$ = createStore({
alex: {age: 20, name: 'Alex'},
john: {age: 30, name: 'John'},
})
const userID$ = user$.map(users => Object.keys(users))
ReactDOM.render(<UserList />, document.getElementById('root'))
Published by zerobias over 5 years ago
event.filter
with common predicate functionsimport {createEvent} from 'effector'
const event = createEvent()
// that event will be triggered only when fn returns true
const filtered = event.filter({
fn: x => x > 0,
})
filtered.watch(x => console.log('x =', x))
event(-2) // nothing happens
event(2) // => x = 2