Published by zerobias over 5 years ago
Published by zerobias over 5 years ago
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 ☄️
Published by zerobias over 5 years ago
store.updates
, representing updates of given store. Use case: watchers, which will not trigger immediately after creation (unlike store.watch
)
import {createStore, is} from 'effector'
const clicksAmount = createStore(0)
is.event(clicksAmount.updates) // => true
clicksAmount.watch(amount => {
console.log('will be triggered with current state, immediately, sync', amount)
})
clicksAmount.updates.watch(amount => {
console.log('will not be triggered unless store value is changed', amount)
})
Published by zerobias over 5 years ago
clearNode
to erase information from the node itself, in addition to the existing opportunity to erase subscribers (thanks @artalar)
Published by zerobias over 5 years ago
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 {createEffect} from 'effector'
import {createComponent} from 'effector-react'
import React from 'react'
const fetchApi = createEffect({
handler: n => new Promise(resolve => setTimeout(resolve, n)),
})
fetchApi.pending.watch(console.log)
const Loading = createComponent(
fetchApi.pending,
(props, pending) => pending && <div>Loading...</div>,
)
fetchApi(5000)
it's a shorthand for common use case
import {createEffect, createStore} from 'effector'
const fetchApi = createEffect()
//now you can use fetchApi.pending instead
const isLoading = createStore(false)
.on(fetchApi, () => true)
.on(fetchApi.done, () => false)
.on(fetchApi.fail, () => false)
sample
. Sample allows to integrate rapidly changed values with common ui statesimport {createStore, createEvent, sample} from 'effector'
import {createComponent} from 'effector-react'
import React from '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 {createStore, createEvent} from 'effector'
import {createComponent} from 'effector-react'
import React from '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, (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)
Published by zerobias over 5 years ago
useEffect
with useLayoutEffect
in useStore
hook to response to state changes immediatelyPublished by zerobias over 5 years ago
import {createStore, createEvent, createStoreObject, combine} from 'effector'
const field = createStore('')
const isEmpty = field.map(value => value.length === 0)
const isTooLong = field.map(value => value.length > 12)
const isValid = combine(
isEmpty,
isTooLong,
(isEmpty, isTooLong) => !isEmpty && !isTooLong,
)
const updateField = createEvent('update field value')
field.on(updateField, (state, upd) => upd.trim())
createStoreObject({
field,
isEmpty,
isTooLong,
isValid,
}).watch(data => {
console.log(data)
})
// => {field: '', isEmpty: true, isTooLong: false, isValid: false}
updateField('bobby')
// => {field: 'bobby', isEmpty: false, isTooLong: false, isValid: true}
Use the new kernel. Provide improved eventual consistency: any side effects will be triggered only after performing all pure computations
Add is
namespace for all type validators
import {createStore, createEvent, is} from 'effector'
const store = createStore('value')
const event = createEvent('some event')
is.store(store) // => true
is.event(store) // => false
is.unit(store) // => true
is.store(event) // => false
is.event(event) // => true
is.unit(event) // => true
is.store(null) // => false
is.event(null) // => false
is.unit(null) // => false
Add clearNode
to break references and subscriptions between events, stores, etc
Add support for custom datatypes by making step
constructors, createNode
and launch
functions public
import {createNode, launch, step, createStore} from 'effector'
const target = createStore(0)
target.watch(n => console.log('current n = ', n))
// => current n = 0
const customNode = createNode({
scope: {max: 100, lastValue: -1, add: 10},
child: [target], // you can forward later as well
node: [
step.compute({
fn: (arg, {add}) => arg + add,
}),
step.filter({
fn: (arg, {max, lastValue}) => arg < max && arg !== lastValue,
}),
step.compute({
fn(arg, scope) {
scope.lastValue = arg
return arg
},
}),
],
})
launch(customNode, 3)
// => current n = 13
launch(customNode, 95)
// no reaction, as 95 + 10 > 100
launch(customNode, 5)
// => current n = 15
launch(customNode, 5)
// no reaction, as we filtered it out with step.filter
fromObservable
, ensure it works with redux
as a typical library with Symbol.observable
supportimport {fromObservable} from 'effector'
import * as redux from 'redux'
const INCREMENT_STATE = 'INCREMENT_STATE'
const reduxStore = redux.createStore((state = 1, action) => {
switch (action.type) {
case INCREMENT_STATE:
return state + 1
default:
return state
}
})
const updateEvent = fromObservable(reduxStore)
updateEvent.watch(state => {
console.log('redux state = ', state)
})
reduxStore.dispatch({type: INCREMENT_STATE})
// => redux state = 1
version
, now it always equals version in package.jsonimport {version} from 'effector'
console.log(version)
// => 0.18.6
import {createEffect, createEvent, forward} from 'effector'
const trigger = createEvent()
const sideEffect = createEffect('side-effect', {
async handler(args) {
await new Promise(rs => setTimeout(rs, 500))
console.log('args: ', args)
},
})
forward({
from: trigger,
to: sideEffect,
})
trigger('payload')
// ~ after 500 ms
// => args: payload
Published by goodmind over 5 years ago
effector
dependencyPublished by goodmind over 5 years ago
useGate
hookimport {createGate, useGate} from 'effector-react'
const Page = createGate('page')
Page.state.watch(({match}) => {
console.log(match)
})
const Home = props => {
useGate(Page, props)
return <section>Home</section>
}
<Route component={Home} />
Published by zerobias over 5 years ago
useStore
hookimport {createStore, createApi} from 'effector'
import {useStore} from 'effector-react'
const counter = createStore(0)
const { increment, decrement } = createApi(counter, {
increment: state => state + 1,
decrement: state => state - 1
})
const Counter = () => {
const state = useStore(counter)
return (
<div>
{state}
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)
}
Published by zerobias over 5 years ago
Fix webpack usage issue. To prevent this in a future, webpack integration test was added.
Improve typescript typings for createApi
. This code example became type checked
import {createStore, createApi} from 'effector'
const text = createStore('')
const {addMessage, cut} = createApi(text, {
addMessage: (text, message) => text + `\n` + message
cut: (text, {fromIndex, size}) => text.slice(fromIndex, fromIndex + size),
})
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/[email protected]/effector.umd.js"></script>
</head>
<body>
<script>
const header = document.createElement('h1')
document.body.appendChild(header)
const text = effector.createStore('hello')
text.watch(str => (header.innerHTML = str))
</script>
</body>
</html>
Published by zerobias over 5 years ago
forward
: common function for forwarding updates and eventsimport {forward} from 'effector'
const unsubscribe = forward({
from: Event | Store,
to: Event | Store | Effect,
})
store.on
import {createStore} from 'effector'
const name = createStore('name')
const counter = createStore(0).on(name, (count, name) => count++)
{handler: Function}
as second argument to createEffect
import {createEffect} from 'effector'
const callApi = createEffect('call api', {
async handler(url) {
const res = await fetch(url)
return res
},
})
effect.use
return the same effect instead of void (ability to chain method calls)import {createEffect} from 'effector'
const callApi = createEffect('call api').use(url => fetch(url))
Published by zerobias almost 6 years ago
store.defaultState
propertycreateComponent
withProps
static functionPublished by zerobias over 6 years ago
import {createDomain} from 'effector'
const mainPage = createDomain('main page')
mainPage.onCreateEvent(event => {
console.log('new event: ', event.getType())
})
mainPage.onCreateStore(store => {
console.log('new store: ', store.getState())
})
const mount = mainPage.event('mount')
// => new event: main page/mount
const pageStore = mainPage.store(0)
// => new store: 0
Published by zerobias over 6 years ago
createWrappedDomain
to watch all nested events and updatesextract
to watch only part of nested storages.epic
method (library supports symbol-observable, so assumed that most.from(event)
or Observable.Of(store)
covered all use cases)Published by zerobias over 6 years ago
effect.use.getCurrent()
method to get current used functioncreateStoreObject
Published by zerobias over 6 years ago
createStore
method.get()
, .set(x)
and .map(fn)
methods)from(effect)