- Breaking: Improved the criteria of when to notify up parents for changes on objects to run only when something inside it has changed, so setting/assigning the same object onto itself will not notify. It's unlikely but possible that may be a breaking change for you if you depended on things re-computing/re-rendering even if nothing changed.
- Breaking: Removed automatically treating DOM nodes and React elements as opaque objects - it added most likely unnecessary extra code and is easily solved in a more generic way. If you're storing those in observables, wrap them in
opaqueObject(...)
. - Feature: Added
is*
functions to export - Perf: Batching was never clearing its safety timeouts, so thousands of changes at once could have been slow
- Breaking: Changed ignoreKeys to be an array to be easier to use
- Breaking: Remove the flexibility for saving arrays and basic objects (can do that with itemID now).
- Feature: IndexedDB supports adjustData, prefixID, itemID, fieldTransforms
- Fix: IndexedDB
loadTable
not being considered loaded if no data was available - Fix: Tons of miscellaneous IndexedDB fixes
- Fix: There was no way to subscribe to updates of dateModified
- Change: When returns the value directly, rather than a Promise, if it's already resolved on the first run
- Fix: mergeIntoObservable not working if source object has only symbol keys
- Fix: Not notifying on array change with the same length but ids added or removed
- Fix: IndexedDB preloader not loading correctly if it has to await the promise
- Fix: Changes deep in an object were not saving to IndexedDB correctly
- Fix: Metadata not saving properly from remote changes
- Breaking: Local Storage is no longer used as default persistence (to reduce build size for those not using it). Please configure persistence at the beginning of your application: https://legendapp.com/open-source/state/persistence/
- Breaking: Moved persist plugins to /persist-plugins export path
- Breaking: Internals of persistence plugins were changed to better support async loading and metadata. If you had made your own persistence plugin the changes should be straightforward, or create an Issue and we'll help migrate it.
- Breaking:
when
behavior tweaked to not be triggered by empty objects or empty arrays - Feature:
ObservablePersistIndexedDB
for persisting to IndexedDB - Fix:
useObserveEffect
not working right in React StrictMode - Types: Improved typing of
For
- Fix:
getObservableIndex
not working on index 0 - Fix:
useObserve
not working properly in StrictMode in React 18
- Fix:
useObservableQuery
was causing re-renders when using mutation
- Fix:
useObservableQuery
still not working right in StrictMode
- Fix:
useObservableQuery
not working in StrictMode
- Feat: Added
useObserveEffect
- Fix: Added useReducer overriding to
createObservableHook
- Feat: Added a
usePersistedObservable
hook - Feat: Added an optional second parameter to observe for an untracked callback function
- Feat: Added helpers:
pageHash
andpageHashParams
(replacesuseHash
) - Fix:
useObservableQuery
was sometimes not working because it was not loading the correct Context - Types: Improved types for strict mode
- Feat: For remote persistence plugins: Add options to disable local or remote sync, support loading remote even if there's no local
- Feat: Added useObservableQuery hook
- Feat: Added local persistence options, starting with mmkv configuration
- Change: Removed persist option from useObservable. It was a bad idea - it imported the whole /persist export. A better solution will come in an update soon.
- Fix:
createObservableHook
was not working with initialState as a function - Perf: Reduce number of renders by not notifying if setting with an unchanged object or array
- Fix: A circular import warning in the react export
- Fix:
useSelector
was not cleaning up when components when components re-rendered from a source other than observables - Types: Improved types for strict mode LegendApp#56
- Feat: Added another way to use the
Switch
component, with multipleShow
children, that renders the firstShow
that matches - Types: Improved types for strict mode LegendApp#52
- Feat: Added
opaqueObject
to make an element in an observable act as a primitive, not proxying its properties or notifying for changes. - Feat: Added some helpers:
observableFetch
,currentTime
,currentDay
- Feat: Added some hooks:
useFetch
,useHash
,useHover
,useIsMounted
,useMeasure
- Feat: Add
getObservableIndex
function to use with the observable argument toFor
- Fix:
reactive
was overriding the given function, causing problems if wrapping an external component and conditionally rendering the original component - Fix:
useObservableReducer
was not working with non-function arguments
- Fix: History not saving the initial object creation
- Fix: Crash when modifying an array was that included as initial value to an observable
- Fix: React Native Switch was not two-way binding properly
- Feat: Added a deps array to useComputed so it can be updated if dependencies change
- Feat: Added reactive types for SVGs
See https://legendapp.com/open-source/state/migrating for more details.
- Breaking: Changed observable
onChange
callback to take an array of changes rather than a single changed value because batched changes were only showing the most recently changed child value. - Breaking: Rename react-components export from legend to Legend
- Feat:
trackHistory
creates an observable that tracks a version history of a target observable - Feat: persistObservable caches pending changes offline so if they're not successfully saved remotely, it attempts to sync them after remote persistence is loaded
- Feat: Allow mergeIntoObservables to delete by using a symbol
- Fix: Types of React Native components were not supporting refs properly
- Fix: Reactive components not forwarding refs properly
- Fix: Tracing functions crashing if component is not an observer
- Fix: mergeIntoObservable was overwriting object children with undefined values
- Fix:
observer
was not auto-memoizing after the rewrite in 0.20.0 - Fix:
For
which a child function auto-observes
- Breaking: Changed behavior of
observe
anduseObserve
so that they have a callback parameter, useful for observing an event and doing something only when it changes. It also has a newprevious
parameter to compare to the previous run which depends on a return value, so the previous behavior using the return value is moved into the callback parameter. If you were returning false to cancel observing, you can now usee.cancel = true
. And if you were returning a cleanup function you can usee.onCleanup = () => ...
. It also adds anum
param to know how many times it's run. - Breaking: Renamed event
dispatch
tofire
- Breaking: Removed deprecated hooking into internal dispatcher
- Breaking: Removed deprecated Bindable components
- Feat: Added a callback parameter to
useObserve
, useful for observing an event and doing something only when it changes - Feat: Added useMount and useUnmount lifecycle hooks to encourage getting away from useEffect
- Feat:
useObserve
has a second callback parameter which will run after the selector. This can be useful for passing anobservable
orevent
as the first parameter. - Fix:
reactive
andobserve
components were sometimes not retaining their static properties (like id). They now use a Proxy wrapper instead of an HOC, which reduces component tree depth and avoids any other bugs from wrapping components and forwarding refs. - Fix:
event
was not working correctly in selectors - Fix: The two-way binding components are always controlled, even if the
value$
is undefined - Types: Improved types of
computed
anduseComponent
to accept a Promise
- Fix: Reactive components were sometimes not working in React Native LegendApp#32
- Feat: Added
itemProps
toFor
component to pass extra props to items - Fix: Setter functions on primitives are auto-bound so you can pass them to event handlers
- Fix: Directly rendering primitive observables was erroring in getNode sometimes
- Feat: Observable booleans have a
toggle()
function - Perf: Observable primities are a simple function instead of a class, reducing code size and should be a bit faster
- Types: Improved typings of For component
- Fix: Persisting primitives
- Feat: Added
useObservableReducer
hook, which is the same asuseReducer
but it updates an observable rather than triggering a render LegendApp#20
- Fix: Fast Refresh not disposing
observe()
correctly LegendApp#25 - Fix: React hooks error in Show when toggling if it has a children function
- Types: Improved types of For to handle computed observables better
- Types: Improved types of useObservable to support Promises
- Feat:
createObservableHook
for converting other hooks that useuseState
to return an observable
- Feat: Support two-way binding multiple props, starting with checked$ on input
This is a big one, with a breaking change to stop observing all React components automatically. See https://legendapp.com/open-source/state/migrating for more details.
- Breaking: No longer observing all React components automatically. Please use
observer
oruseSelector
for observable tracking. - Breaking: Primitives no longer have
value
- use the standardget()
orset()
instead - Breaking: Removed
get(false)
in favor ofpeek()
- Deprecated: Bindable components will be phased out in favor of new reactive components. Import
{ legend }
on web or{ Legend }
on react-native instead of Bindable. - Feat: Added
observer
HOC component - Feat:
reactive
components that let you pass an observable or selector to any prop reactive-props - Feat:
useSelector
has options to control how often it renders and to reuse forceRender functions - Fix: Improved types for TypeScript strict mode
- Fix: Local storage persistence removes item if undefined
- Fix: Rendering multiple obseravbles inside one element had key collision issues
- Fix: Array move detection further improved
- Feat:
observe
function can return false to prevent tracking - Fix: Tracking was sometimes getting out of order with nested components and computed
- Fix: useSelector was triggering renders multiple times
- Fix: Array move detection was incorrect on inserts
- Fix: React-specific props were creating proxies unnecessarily
- Fix: Fast refresh issues with bindable components
- Fix: Fast refresh issues with direct rendering
- Fix: Rendering directly to JSX was not activating computeds
- Types: Improved typing of observable and useObservable to more correctly narrow down complex types
- Fix: Crash in Switch component in development
- Breaking: Renamed tracing functions to
use*
to match hook naming - Fix: Improved automatic React hooking into dispatcher to not need a
useEffect
and more dependably cleanup - Fix: Better handling JSX and DOM elements in observables
- Fix: Tracing functions were not always working correctly
- Fix: Errors building in Next.js
- Fix: Typing of Show component with Babel plugin enabled
- Feat: Support
computed
with a Promise
- Fix: React behavior disabled until it's activated by React rendering it
- Fix: Missing export of
useSelector
- Fix: Improve
observe()
disposing
- Fix: Undefined observables were not rendering directly in React properly
- Fix:
observe()
was not updating listeners on each run
- Fix: Typo in mergeIntoObservable
- Fix: Wrapped React hook injection in try/catch because it was sometimes causing errors like when hydrating in Next.js
This is a big one, with mainly a breaking change to how primitives work, so see https://legendapp.com/open-source/state/migrating for more details.
- Breaking: Primitives in state are now returned as observable objects like everything else, and you can use
get()
or.value
to access/modify the value - Breaking: Removed
obs()
function - Breaking:
set()
no longer has a keyed version because it's not needed now that we can dot through undefined nodes - Breaking: Renamed
useComputed
touseSelector
- Feature: Because primitives are returned as observables, we can now dot through undefined nodes
- Feature: Added
peek()
function, which is the same asget(false)
- Feature:
useComputed
returns acomputed
observable - Feature:
useObserve
creates anobserve
context - Feature:
computed
is now lazy and won't activate until its value is accessed for the first time - Feature:
Show
has awrap
prop to wrap children, for example with - Feature: Allow observable with no parameters, initialized to undefined
- Feature:
verifyNotTracking()
to make sure that components never re-render - Perf: Observables created as primitives use a class instead of a Proxy, to speed up the scenario of using tons of primitive observables
- Perf: Listeners that don't care about the value (like observe and React components) skip passing all the parameters to callbacks
- Fix: The new
enableLegendStateReact()
is more stable and works better with nested components - Fix: Rendering observables directly is more stable, especially in React Native
- Fix: Modifying listeners in an
observe
was sometimes causing infinite loops
- Fix: A component going from tracking nodes to not tracking nodes was causing errors
See https://legendapp.com/open-source/state/migrating for more details.
- Breaking: Removed
observer
HOC - Feat: No longer need
observer
HOC - CallenableLegendStateReact()
at the beginning of your app, and then all components automatically observe any accessed state - Feat:
when
callback receives the current value as the parameter - Feat: Add join to array functions that create shallow listeners
- Feat: Observables can easily switch back and forth between being an object or a primitive, and observable primitives have less overhead
- Fix: Crash when creating an observable starting undefined
- Fix: Assigning an object with function children
This is a big one with many breaking (but good) changes, so see https://legendapp.com/open-source/state/migrating for more details. We're making a lot of breaking changes all once so it's not too impactful.
- Breaking: There are now three levels of safety: Unsafe, Default, Safe. "Default" is new and allows direct assignment to primitives but prevents directly assigning to everything else. The previous default behavior was "Unsafe" so you may see errors if you were directly assigning to objects/arrays/etc... Replace those with
.set(...)
or pass infalse
as the second parameter toobservable
to go back to "Unsafe" mode. - Breaking: Renamed
ref()
toobs()
- Breaking: The array optimizations are now opt-in, because they can potentially have some unexpected behavior if modifying the DOM externally. You can enable them by using the
For
component with theoptimized
prop. - Breaking: Replaced
shallow
with aTracking
namespace, to add Optimized tracking. Changeshallow
toTracking.shallow
to get the previous behavior, orTracking.optimized
on an array to get the optimized behavior. - Breaking: Changed
observableBatcher
to export the batching functions directly instead of as a namespace - Breaking: Removed
onChangeShallow
,onTrue
,onEquals
, andonHasValue
in favor of the neweffect
andwhen
which automatically track any accessed observables. - Breaking: Renamed primitive observables' wrapping value from
current
tovalue
. - Breaking: Renamed
observableComputed
tocomputed
andobservableEvent
toevent
. - Breaking: Renamed the bindable components from
LS
toBindable
and they now export from '@legendapp/state/react-components' or '@legendapp/state/react-native-components' - Feat: Observable primitives can be rendered directly in React
- Feat: Added
observe
, which can run arbitrary code while tracking all accessed observables. - Feat: Added
when
, which can run functions when the predicate returns a truthy value. - Feat: Added
Switch
component - Feat: Support creating an observable with a Promise as a value, which will update itself when the promise resolves.
- Feat: A
lockObservable
function to prevent writes - Fix: Observables with arrays at the root were not notifying listeners properly
- Fix: Accessing
current
(nowvalue
) on a primitive observable was not tracking as expected - Fix: Improve types of Memo/Computed/Show components so that they require functions by default, and are expanded to not need functions when referencing the babel types.
- Feat: Allow passing observables directly to Show
- Fix: Usage of old observe() when if prop is an observable
- Fix: Some issues in remote persistence plugins (not yet released)
- Fix: Some issues in remote persistence plugins (not yet released)
- Fix: Old versions of React Native were crashing because of using
React.
without importing it
- Fix:
For
component with children re-renders with the latest children correctly
- Feature: A
For
component for easy rendering with array optimizations - Fix: Improve performance of observer
- Fix: Support
_id
or__id
field names for array optimizations - Fix: Mixing shallow and non-shallow listeners in a component could have not mixed correctly
- Types: Renamed exported types for improved clarity
- Fix: Exported components were losing className/style when not using bind prop
- Breaking Change: Removed observe() and prop(), favoring get() and ref(). get() tracks by default and ref() does not.
- Feat: Support ref to a path on an undefined value
- Fix: A crash when calling get() on an observable with undefined parents
- Types: Enforce bind prop to not be a primitive
- Types: Improved types of exported components
- Feat: Allow direct assignment, with warnings to catch accidental errors, and an optional "safe" mode
- Feat: input components with
bind
prop that automatically binds an observable to value and onChange - Feat: Support keyed ref:
obs.ref('key')
- Feat:
onChange
has arunImmediately
option - Fix:
.ref()
and.get()
inside anobserver
do reference counting so they don't untrack too aggressively - Fix:
delete()
was notifying listeners with the value undefined, but the key not yet deleted - Fix:
observer
was sometimes missing updates occurring between render and mount
- Fix: New set option with function parameter was breaking persistence
- Fix: Component useEffect was getting called before observer could listen for changes
- Fix: Babel plugin adds imports only once, only if not already imported
- Feat:
set()
can take a function to easily compute it relative to the previous value
- Feat: Added
traceListeners
andtraceUpdates
functions (exported from @legendapp/state/trace). Call them within an observer.traceListeners
logs the path of all tracked observables, whiletraceUpdates
logs details of each observable change that causes a render.
- Fix: observer was not working the first time in StrictMode
- Fix: observer was not cleaning up old listeners when the the tracked observables changed