effector-react
Advanced tools
Changelog
effector 20.0.0
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 fetchApiFx = createEffect({
handler: n =>
new Promise(resolve => {
setTimeout(resolve, n, `${n} ms`)
}),
})
fetchApiFx.finally.watch(response => {
console.log(response)
})
await fetchApiFx(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.*
re-exportsChangelog
effector-react 19.1.2
useStoreMap
hook to select part from a store. Motivationimport {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: $users,
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'))
Changelog
effector 19.1.0
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
Changelog
effector, effector-react, effector-vue 19.0.0
To indicate the stability of the project, we adopting semantic versioning and happy to announce version 19.0.0 for all packages. And to make the transition easier, that release contains no breaking changes; simple replacement of "^0.18.*" to "^19.0.0" is safe for sure ☄️
Changelog
effector-react 0.18.10
createComponent
import {users} from './feature'
const UserItem = createComponent(
initialProps => users.map(users => users[initialProps.id]),
(_, user) => {
return <div>{user.username}</div>
},
)
const UserList = createComponent(users, (_, users) => {
return users.map(user => <TodoItem key={user.id} id={user.id} />)
})
createStoreObject
in createComponent
const deposit = createEvent()
const $username = createStore('zerobias')
const $balance = createStore(0)
const Profile = createComponent(
{username: $username, balance: $balance},
(_, {username, balance}) => {
return (
<div>
Hello, {username}. Your balance is {balance}
<button onClick={deposit}>Deposit</button>
</div>
)
},
)
mounted
and unmounted
events to components created by createComponent
import {counter, increment} from './feature'
const Counter = createComponent(counter, (_, state) => {
return (
<div>
{state}
<button onClick={increment}>+</button>
</div>
)
})
Counter.mounted.watch(({props, state}) => {
counter.on(increment, s => s + 1)
})
Counter.unmounted.watch(({props, state}) => {
counter.off(increment)
})
useLayoutEffect
with useIsomorphicLayoutEffect
to support server-side renderingChangelog
effector 0.18.9
clearNode
to erase information from the node itself, in addition to the existing opportunity to erase subscribers (thanks @artalar)
Changelog
effector 0.18.7-0.18.8
store.reset
import {createStore, createEvent} from 'effector'
const firstTrigger = createEvent()
const secondTrigger = createEvent()
const $target = createStore(0).reset(firstTrigger, secondTrigger)
Add support for createEvent
and createEffect
with config (see next code example)
Add .pending
property for effects
import React from 'react'
import {createEffect} from 'effector'
import {createComponent} from 'effector-react'
const fetchApiFx = createEffect({
handler: n => new Promise(resolve => setTimeout(resolve, n)),
})
fetchApiFx.pending.watch(console.log)
const Loading = createComponent(
fetchApiFx.pending,
(props, pending) => pending && <div>Loading...</div>,
)
fetchApi(5000)
it's a shorthand for common use case
import {createEffect, createStore} from 'effector'
const fetchApiFx = createEffect()
//now you can use fetchApiFx.pending instead
const $isLoading = createStore(false)
.on(fetchApiFx, () => true)
.on(fetchApiFx.done, () => false)
.on(fetchApiFx.fail, () => false)
sample
. Sample allows to integrate rapidly changed values with common ui statesimport React from 'react'
import {createStore, createEvent, sample} from 'effector'
import {createComponent} from 'effector-react'
const tickEvent = createEvent()
const $tick = createStore(0).on(tickEvent, n => n + 1)
setInterval(tickEvent, 1000 / 60)
const mouseClick = createEvent()
const $clicks = createStore(0).on(mouseClick, n => n + 1)
const sampled = sample($tick, $clicks, (tick, clicks) => ({
tick,
clicks,
}))
const Monitor = createComponent(sampled, (props, {tick, clicks}) => (
<p>
<b>tick: </b>
{tick}
<br />
<b>clicks: </b>
{clicks}
</p>
))
const App = () => (
<div>
<Monitor />
<button onClick={mouseClick}>click to update</button>
</div>
)
import React from 'react'
import {createStore, createEvent} from 'effector'
import {createComponent} from 'effector-react'
const $title = createStore('welcome')
console.log('store.shortName', $title.shortName)
// store.shortName title
const clickTitle = createEvent()
console.log('event.shortName', clickTitle.shortName)
// store.shortName clickTitle
const Title = createComponent({title: $title}, (props, title) => (
<h1>{title}</h1>
))
console.log('Component.displayName', Title.displayName)
// Component.displayName Title
Plugins are available out from a box
.babelrc
:
{
"plugins": ["effector/babel-plugin", "effector/babel-plugin-react"]
}
import {createStore, createEvent} from 'effector'
const updates = createEvent()
const $state = createStore(0)
$state.watch(updates)
Changelog
effector 0.18.7-0.18.8
store.reset
import {createStore, createEvent} from 'effector'
const firstTrigger = createEvent()
const secondTrigger = createEvent()
const $target = createStore(0).reset(firstTrigger, secondTrigger)
Add support for createEvent
and createEffect
with config (see next code example)
Add .pending
property for effects
import React from 'react'
import {createEffect} from 'effector'
import {createComponent} from 'effector-react'
const fetchApiFx = createEffect({
handler: n => new Promise(resolve => setTimeout(resolve, n)),
})
fetchApiFx.pending.watch(console.log)
const Loading = createComponent(
fetchApiFx.pending,
(props, pending) => pending && <div>Loading...</div>,
)
fetchApi(5000)
it's a shorthand for common use case
import {createEffect, createStore} from 'effector'
const fetchApiFx = createEffect()
//now you can use fetchApiFx.pending instead
const $isLoading = createStore(false)
.on(fetchApiFx, () => true)
.on(fetchApiFx.done, () => false)
.on(fetchApiFx.fail, () => false)
sample
. Sample allows to integrate rapidly changed values with common ui statesimport React from 'react'
import {createStore, createEvent, sample} from 'effector'
import {createComponent} from 'effector-react'
const tickEvent = createEvent()
const $tick = createStore(0).on(tickEvent, n => n + 1)
setInterval(tickEvent, 1000 / 60)
const mouseClick = createEvent()
const $clicks = createStore(0).on(mouseClick, n => n + 1)
const sampled = sample($tick, $clicks, (tick, clicks) => ({
tick,
clicks,
}))
const Monitor = createComponent(sampled, (props, {tick, clicks}) => (
<p>
<b>tick: </b>
{tick}
<br />
<b>clicks: </b>
{clicks}
</p>
))
const App = () => (
<div>
<Monitor />
<button onClick={mouseClick}>click to update</button>
</div>
)
import React from 'react'
import {createStore, createEvent} from 'effector'
import {createComponent} from 'effector-react'
const $title = createStore('welcome')
console.log('store.shortName', $title.shortName)
// store.shortName title
const clickTitle = createEvent()
console.log('event.shortName', clickTitle.shortName)
// store.shortName clickTitle
const Title = createComponent({title: $title}, (props, title) => (
<h1>{title}</h1>
))
console.log('Component.displayName', Title.displayName)
// Component.displayName Title
Plugins are available out from a box
.babelrc
:
{
"plugins": ["effector/babel-plugin", "effector/babel-plugin-react"]
}
import {createStore, createEvent} from 'effector'
const updates = createEvent()
const $state = createStore(0)
$state.watch(updates)