## 3.0.0-beta.19 to 3.0.0-beta.20

### observer -> useValue

Based on discussions with the React Compiler team and a lot of feedback from the community, we're changing the suggested primary way of using observables in React. The old ways will still work for a while so we don't break existing apps and we have some tools to aid in the migration, which can be done slowly over time.

Basically, we need to change from `observer` tracking all `get()` calls to using a `useValue` hook instead, which is just a renamed `useSelector`.

#### useSelector to useValue

`useSelector` is renamed to `useValue`, because the term "Selector" has a lot of baggage from other state libraries and many new users found it confusing. `useSelector` will still work for a while so you can make the change slowly if you want, or a global find and replace should work.

```jsx
// 🔴 From
const value = useSelector(state$.value)
// ✅ To
const value = useValue(state$.value)
```

#### observer to useValue

We are now encouraging using the `useValue` hooking rather than using `observer` to track all `get()` calls. `observer` will still work for a while so you can migrate slowly. But we have rewritten the docs to use `useValue` everywhere and will be focusing on that going forward.

The reason for this is that `observer` is not compatible with React Compiler. To work best with the Compiler, render functions need to be pure, meaning that calling a function (like `state$.get()`) should always return the same value. So based on that assumption, Compiler will wrap function calls in `useMemo`. But Legend State's current usage of `observer` depends on each `state$.get()` function call to returning a different value when it changes, so memoizing its value would break Legend State's reactivity.

But, Compiler will not memoize any hooks (functions starting with "use"), so if we just use a `useValue(state$)` hook instead of `state$.get()` then everything will work fine. So because Compiler is a really great optimization already in its first version and will continue to get better over time, we want to be perfectly compatible with the Compiler to get all of the benefits.


```jsx
const state$ = observable({ value: 10 })

// observer is now just an optional performance optimization
const Component = observer(() => {
    // ✅ The new way
    const value = useValue(state$.value)

    // 🔴 The old way
    const value = state$.value.get()
})
```

#### The full details

- ✅ A new hook `useValue` (just `useSelector` with a new name) is now the default way to consume observables

```jsx
const state$ = observable({ value: 10 })
const Component = () => {
    const value = useValue(state$.value)
    // ...
}
```

- ✅ observer is an optimization to merge all `useValue` calls into a single hook, useful for large components

```jsx
const state$ = observable({ value1: 10, /* ... */ })
const Component = observer(() => {
    // Observer makes this run only a single hook
    const value1 = useValue(state$.value1)
    const value2 = useValue(state$.value2)
    const value3 = useValue(state$.value3)
    const value4 = useValue(state$.value4)
    const value5 = useValue(state$.value5)
    const value6 = useValue(state$.value6)
    const value7 = useValue(state$.value7)
    // ...
})
```

- ✅ If calling a function that consumes observables, wrap it in `useValue`. This will have an additional benefit of only triggering re-renders if the return value changes.

```jsx
const state$ = observable({ v1: 10, v2: 20 })
const getComputedData = () => {
    return state$.v1.get() + state$.v2.get()
}
const Component = () => {
    const v = useValue(() => getComputedData())
    // ...
}
```

- 🔴 (Deprecated) Calling `.get()` directly within `observer`

```jsx
const state$ = observable({ value: 10 })
const Component = observer(() => {
    const value = state$.value.get()
    // ...
})
```

- 🔴 (Deprecated) `enableReactTracking({ auto: true })` is deprecated and will be removed in a later version

This was a shortcut to use `get()` without needing observer, but since we're discouraging `get()` anyway, this becomes less useful. It's broken in React 19, so we're deprecating it rather than try to fix it for no ongoing benefit.

```jsx
enableReactTracking({ auto: true })
const state$ = observable({ value: 10 })
const Component = () => {
    const value = state$.value.get()
    // ...
}
```

### Reactive -> $React and individual exports

Based on conversations with users of reactive components in React Native, many were finding it confusing to have to configure to enable them, and the typing of `Reactive` did not work very well. Plus we want to make sure that bundle size is minimized so we don't need to include the web code in React Native. So we're just going to do a more standard thing and have different exports.

In React on web there is now a `$React` namespace that includes all of the DOM components. And on React Native there's separate exports for each of the built-in components.

#### Web

```tsx
import { observable } from "@legendapp/state";
import { $React } from "@legendapp/state/react-web"

const state$ = observable({ age: 8 });

function Component() {
    return (
        <$React.div
            $className={() => state$.age.get() > 5 ? 'kid' : 'baby'}
        />
    )
}
```

#### React Native

```tsx
import { observable } from "@legendapp/state";
import { $TextInput } from "@legendapp/state/react-native"

const state$ = observable({ name: "Taylor" });

function Component() {
    return (
        <$TextInput
            $value={state$.name}
        />
    )
}
```


## 2.x to 3.0 Beta

Version 3.0 is a big change focused mainly in four areas. If you have any trouble with the migration join the community on [Discord](https://discord.gg/5CBaNtADNX) or [Github](https://github.com/LegendApp/legend-state) and we will help.

### 1. Types

The types were fully rewritten from scratch, fixing many bugs especially around Promises and nullability, and many of the types were renamed. You may need to rename some of your type imports.

### 2. Computed/Proxy

computed/proxy are no longer separate concepts, they're just a function in an observable. If you were using `computed` you can just use `observable` now, and if you were using `computed` as a child of an observable, you can remove `computed` and use just a function. The old behavior will still work for now, but the new way is more efficient and `computed` will be deprecated in a later version.

See [Computed Observables](../../usage/observable#computed-observables) for more details.

```ts
// Change
const compValue$ = computed(() => /* ... */)
// to
const compValue$ = observable(() => /* ... */)

// Change
const state$ = observable(() => ({
    value: 1,
    comp: computed(() => state$.value.get())
}))
// to
const state$ = observable(() => ({
    value: 1,
    comp: () => state$.value.get()
}))
```

`proxy` is now just a function with a string parameter.

See [Computed Observables](../../usage/observable#lookup-table) for more details.

```ts
// Change
const state$ = observable({
    items: { test1: { text: 'hi' }, test2: { text: 'hello' } },
    itemText: proxy((key) => {
        return state$.items[key]['text'];
    })
})
// To
const state$ = observable({
    items: { test1: { text: 'hi' }, test2: { text: 'hello' } },
    texts: (key: string) => {
        return state$.items[key]['text']
    }
})
```

And you can use `useObservable` in the same way that you used to use `useComputed`:

```ts
// Change
const state$ = useComputed(() => state$.text.get())
// To
const state$ = useObservable(() => state$.text.get())
```

### 3. Persist

Persistence works in the same way but the API is changed for clarity and new features with the new more robust sync engine. `persistObservable` is changed to `syncObservable` and the persisting features are under the `persist` option instead of `local`.

Additionally, `persistObservable` was inserting a `state` property for the sync state, which caused compatibility issues with data that had its own `state` property. So the sync state is now accessible separately with `syncState`.

Global configuration was confusing and restricting, so instead of a global `configureObservablePersistence` you can now create customize synced functions.

```ts
import { syncState } from "@legendapp/state"
import { configureObservablePersistence, persistObservable } from "@legendapp/state/persist"
import { ObservablePersistLocalStorage } from "@legendapp/state/local-storage"
import { configureSynced, syncObservable } from "@legendapp/state/sync"
// Change
configureObservablePersistence({
    pluginLocal: ObservablePersistLocalStorage
})
const { state } = persistObservable(state$, {
    local: {
        name: "store",
    }
})
// To
const syncPlugin = configureSynced({
    persist: {
        plugin: ObservablePersistLocalStorage
    }
})
syncObservable(state$, syncPlugin({
    persist: {
        name: "store",
    }
}))
const state = syncState(state$);
```

### 4. Sync

It's now much easier to sync individual observables with a new plugin engine. See the [Persist and Sync docs](../../sync/persist-sync/) for the latest documentation.

If you had made a custom sync plugin on the old system, please post an issue on [Github](https://github.com/LegendApp/legend-state) or talk to us on [Discord](https://discord.gg/5CBaNtADNX), and we will help you with the migration.

### 5. Removed old sync plugins

Version 2 had inconsistent and varied ways to sync with fetch or Query, and they are now aligned into the Sync engine.

- Fetch: [syncedFetch](../../sync/fetch)
- TanStack Query: [syncedQuery](../../sync/tanstack-query)

### Other changes

- useObservable with a function parameter is now reactive like useComputed was. So use peek() when accessing observables inside it if you want it to be just an initial value and not be reactive.
- Computeds now only re-compute themselves when observed. This may cause some migration issues if your computeds had side effects, as they will not re-run when dependencies change unless being observed.
- Removed lockObservable: With the new method of computeds it's not possible to modify the types to be readonly, so we removed this feature.
- set and toggle return void: They had previously returned the observable in order to allow chaining, but it caused unintended side effects, so they now return void.
- `onSet` was renamed to `onAfterSet` for clarity
- Removed the concept of "after batch" - it was generally unreliable because batches can run recursively
- Renamed `enableDirectAccess()` to `enable$GetSet()` and `enableDirectPeek()` to `enable_PeekAssign()` for clarity
- Moved `trackHistory` export, so you can now use  `import { trackHistory } from '@legendapp/state/helpers/trackHistory'`


## 1.x to 2.0

Version 2.0 removes old deprecated features to reduce bundle size and encourage everyone to move over to the new features. Version 1.11 displays deprecation warnings to help you migrate.

So there are two migration strategies:

1. **Runtime**: Upgrade to version 1.11 and check the console for warnings whenever using deprecated features. This can give you time to do the migration slowly without breaking anything.
2. **Build time**: Upgrade to version 2.0 and use TypeScript warnings to find errors

These are all things that were changed over time between 1.0 and 2.0 so depending on when you started using Legend-State you may already be doing it the new way.

### Promise behavior changed

When setting a Promise into an observable it creates a `state` child on the observable with `isLoaded` and `error` properties that you can check for load state. After it resolves or rejects it replaces itself with the resolved value and updates the `state`. Previously in the case of an error it would replace itself with an `{ error }` object.

So if you had logic to check whether a Promise errored by checking the `error` property on the observable, you can change that to `.state.error`.

### enableLegendStateReact, enableReactDirectRender => Memo

The direct rendering enabled by `enableLegendStateReact` and `enableReactDirectRender` was fragile, hard to find in files, and the React team advised against it. So instead we are using the `Memo` component. See [Render an observable directly](../../react/fine-grained-reactivity#render-an-observableselector-directly) for more details.

To migrate you can remove usage of `enableLegendStateReact()` or `enableReactDirectRender()` as well as any usage of direct rendering, and replace it with `Memo`. When you remove those imports it will stop overriding the types so rendering observables directly will result in TypeScript errors.

If you were using `enableLegendStateReact` to render direct observables heavily, [enableReactDirectRender](../../usage/configuring#enablereactdirectrender-deprecated) is still usable to ease migration, though it will throw TypeScript errors to help you migrate away. It will be fully removed in version 3.0.

```jsx
// Remove these:
enableLegendStateReact();
enableLegendStateReact();

function Component() {
  const text$ = useObservable("test");
  return (
    <>
      Change this: {text$}
      To this: <Memo>{text$}</Memo>
    </>
  );
}
```

### Legend components changed to Reactive components

The reactive components are now better named and more easily customizable with configuration functions, exported from the normal `/react` path. See [Reactive components](../../react/fine-grained-reactivity#reactive-components) for more details.

Change:

```js
// React
import { Legend } from "@legendapp/state/react-components";
function Component() {
  return <Legend.div>...</Legend.div>;
}
// React Native
import { Legend } from "@legendapp/state/react-native-components";
function Component() {
  return <Legend.View>...</Legend.View>;
}
```

To:

```js
// React
import { enableReactComponents } from "@legendapp/state/config/enableReactComponents";
enableReactComponents();

// React Native
import { enableReactNativeComponents } from "@legendapp/state/config/enableReactNativeComponents";
enableReactNativeComponents();

// Now you can use them anywhere
import { Reactive } from "@legendapp/state/react";

function Component() {
  // React
  return <Reactive.div>...</Reactive.div>;

  // React Native
  return <Reactive.View>...</Reactive.View>;
}
```

### For parameter name changed

As we've been adopting a convention of naming observables suffixed with `$`, many users were confused by the `item` prop in the render component used in For's `item` prop, so we renamed it to `item$` to make it clear that it's an observable. `item` will still work until version 3.0, but it will throw TypeScript errors to encourage changing it to `item$`.

### Reactive props changed to start with $

In an earlier version reactive props ended with $ and were changed in version 1.3.0 to allow starting with $, because it has a better UX with autocomplete and is easier to visually scan for. Both have been supported but version 2.0 will remove type support for the suffix version. It will still work in code so it doesn't break your apps, but we will fully remove it in 3.0 and suggest you at least start using the new pattern.

A recommended way to find and replace all of instances of the old method is to find `$=` in all files.

```jsx
import { Reactive, useObservable } from "@legendapp/state/react";

function Component() {
  const text$ = useObservable("test");
  return (
    <Reactive.div
      // Change this
      className$={() => "..."}
      // to this
      $className={() => "..."}
    >
      ...
    </Reactive.div>
  );
}
```

### Persistence Changes

In version 2 we locked down and cleaned up the interfaces for the remote persistence APIs.

#### persistObservable returns an object with sync state

To support taking an initial state or an observable, `persistObservable` needs to return both an observable and the syncState, so it now the observable with a `state` property on it, matching the Promise behavior.

See the [Persist & Sync guide](../../sync/persist-sync#persist-data-locally) for details.

```js
const { state } = persistObservable(initialStateOrObservable, { ... })
```

#### persistLocal => pluginLocal

We renamed the parameters for clarity as the difference between `local` and `persistLocal` wasn't clear. So it is now named `pluginLocal` because that makes more sense.

```js
persistObservable(initialStateOrObservable, {
    // Before
    persistLocal: ObservablePersistLocalStorage
    // After
    pluginLocal: ObservablePersistLocalStorage
 })
```

#### Remote options renamed

Since there were not any remote persistence plugins before, these changes would likely not affect you unless you made your own. See [Sync with a server](../../sync/persist-sync#sync-with-a-server) for details.

### observer, reactive, reactiveObserver not exported from react-components

The `/react-components` export was mistakenly exporting `observer`, `reactive`, and `reactiveObserver` which are already exported from `/react`. Your editor may have automatically imported from `/react-components` so may need to be changed.

```diff
-import {
-  observer,
-  reactive,
-  reactiveObserver,
-} from "@legendapp/state/react-components";
+import { observer, reactive, reactiveObserver } from "@legendapp/state/react";
```

### types.d.ts moved to types/babel.d.ts

`types.d.ts` was too generic of a name now that we have a lot of configuration options, so we are naming them more specifically in a "types" folder. For now there's still only the `babel` types but this gives room to add more in the future.

```js
// Change this:
/// <reference types="@legendapp/state/types" />

// To this:
/// <reference types="@legendapp/state/types/babel" />
```

### afterBatch removed

`afterBatch` was not working 100% correctly in all cases, and the best way to fix it was to make it part of `batch(...)`.

```js
// Change
beginBatch();
afterBatch(() => {
  console.log("done");
});
obs$.set(true);
endBatch();

// To
batch(
  () => {
    obs$.set(true);
  },
  () => {
    console.log("done");
  }
);
```

## 0.23 to 1.0

### onChange changed

1. `onChange` now takes a second object parameter with `trackingType` and `initial` options. If you were using a second parameter (like `true` to track shallowly) before, use `{ trackingType: true }`.

2. The `onChange` callback now receives an object with `value`, `getPrevious`, and `changes` in it, replacing the previous multiple arguments.

These changes allow for more flexibility - it's easier for callers who care about the changes but not the current value or previous values (like persistence plugins), and the new `initial` option lets it behave more like `observe` where it runs immediately instead of waiting for a change.

```js
// Old
obs.onChange((value, getPrevious, changes) => {
  // ...
}, true);

// New
obs.onChange(
  ({ value, getPrevious, changes }) => {
    // ...
  },
  { trackingType: true }
);
```

### when and Show tweaked

They were previously checking if the value is "ready", meaning it doesn't count if it's an empty object or empty array. They now do a standard javascript truthiness check as would be expected. For the previous behavior you can use `whenReady` or `<Show ifReady={...}>`

### IndexedDB preloader removed

It was actually slower in our testing so we simplified things and just removed it. See [IndexedDB](../../sync/persist-sync#indexeddb-react) for up-to-date docs.

## 0.22 to 0.23

### Setting an observable object to the same value no longer notifies

Setting an object to itself was triggering notifications, which is not great for performance and is undesirable in most cases. It is now more targeted and will only notify on elements that actually changed. It's unlikely that will affect you, but it may be a breaking change for you if you depended on things re-computing/re-rendering even if nothing changed.

### Not automatically treating DOM nodes and React elements as opaque objects

It was adding 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(...)`.

### IndexedDB plugin support for non-dictionaries removed

For flexibility of multiple observables persisting to the same IndexedDB table, it now has an `itemID` option to save non-dictionaries. So the IndexedDB persistence plugin can be used in two ways:

1. Persisting a dictionary where each value has an `id` field, and each value will create a row in the table
2. Persisting multiple observables to their own rows in the table with the `itemID` option

```js
const settings = observable({ theme: "light" });
persistObservable(settings, {
  local: {
    name: "store",
    indexedDB: {
      itemID: "settings",
    },
  },
});
```

## 0.21 to 0.22

### Local Storage is no longer the default persistence

This was changed to reduce build size for those who don't use it. If you want to use Local Storage, configure it at the beginning of your app:

```js
import { configureObservablePersistence } from "@legendapp/state/persist";
import { ObservablePersistLocalStorage } from "@legendapp/state/local-storage";

configureObservablePersistence({
  persistLocal: ObservablePersistLocalStorage,
});
```

### Moved persist plugins to `/persist-plugins`

Update your import paths:

```text
import { ObservablePersistLocalStorage } from "@legendapp/state/persist-plugins/local-storage";
import { ObservablePersistMMKV } from "@legendapp/state/persist-plugins/mmkv";
```

### `when` is not triggered by empty {} or []

If you wanted `when` to be triggered by those, you can update it to use a selector to return a boolean like:

```js
const obs = {}
when(() => !!obs, () => {...})
```

## 0.20 to 0.21

### Changed `onChange` callback

The extra paremeters in the `onChange` callback have changed to include an array of of the changes, fixing a bug where it was only showing the latest child's change when changing multiple children while batching. This likely won't affect many of you as it's mostly intended for internal use and persistence plugins.

## Renamed React components from `legend` to `Legend`

We had originally used lower casing to match html elements, but in practice it did not autocomplete well and felt wrong. So just rename to uppercase, for example from `<legend.div />` to `<Legend.div />`.

## 0.19 to 0.20

### Removed deprecated automatic observing

The automatic observing from 0.18 was deprecated in 0.19 and is now removed. See [Deprecated automatic observing](#deprecated-automatic-observing).

### observe and useObserve

As `observe` has gotten more and more powerful, it outgrew modifying behavior based on the return value, so it now has an event parameter to control canceling listening and a cleanup function.

- If you were returning false to cancel observing, you can now use `e.cancel = true`.
- If you were returning a cleanup function you can use `e.onCleanup = () => ...`.
- It also adds a `num` param to know how many times it's run and a `previous` param to compare to the previous value.

```js
observe((e) => {
    // Cancel observing any future changes
    e.cancel = true

    // A cleanup function
    e.cleanup = () => ...
})
```

### Renamed event `dispatch` to `fire`

Just change `evt.dispatch()` to `evt.fire()` and all is good 👍.

## 0.18 to 0.19

### Deprecated automatic observing

We are deprecating the automatic observing that depended on hooking into React's internals. Components will no longer track observables automatically, but you can easily do it per component in a few ways:

- Wrap components in `observer` to make them track automatically
- Wrap observable access in `useValue` to return a value and track automatically.
- Render observables directly into JSX.

So tracking observables in React can look like this now:

```jsx
import { observable } from "@legendapp/state";
import { observer } from "@legendapp/state/react";

const state$ = observable({ value: 0 });

const Component = observer(function Component() {
  const value = state$.value.get();
  // This tracks because it's inside an observer
  console.log(value);
  return null;
});
```

or

```jsx
import { observable } from "@legendapp/state";
import { useValue } from "@legendapp/state/react";

const todo$ = observable({ done: false });

function Component() {
  // Track the value of an observable
  const todo = useValue(todo$);

  // Track the return value of a function
  const isDone = useValue(() => todo$.done.get());

  console.log(todo, isDone);
  return null;
}
```

See the [React API guide](../../react/react-api) for how we suggest setting up your components now.

Rendering observables directly still works though, and `enableLegendStateReact()` still enables that.

You can still enable the previous behavior for now with `enableLegendStateReact({ autoTrackingDEPRECATED: true })` while you migrate to using `observer` or `useValue`. That option will be removed before we reach 1.0.

#### Why

- It doesn't actually work. We thought this method would be safe to use because it was inspired by Preact Signals, but as we've integrated Legend-State into more environments we found significant edge cases that seem to be unfixable and suggest that the whole concept is just unworkable.
- The React team has asked us not to do it and made it clear that it is likely to break in a future version of React.
- As Legend-State has evolved, the ideal way of using it has shifted towards fine-grained reactivity where components render minimally or only once, and we were actually specifically opting out of auto-tracking more often than not. So in the interest of pursuing the render-once ideal, we think it's actually generally better to use the [reactivity components](../../react/fine-grained-reactivity) or opt-in to tracking.
- We don't want to distract from the core mission of Legend-State with an unreliable and unstable core.

### Bindable components deprecated

We now have a more general purpose way of making reactive props that can also be used for two-way binding for inputs. So change:

```jsx
<Bindable.input bind={state$} />
```

to

```jsx
import { observable } from "@legendapp/state";
import { Legend } from "@legendapp/state/react-components";

const state$ = observable("");

<Legend.input value$={state$} />;
```

See [reactive props](../../react/fine-grained-reactivity) for more detauls.

### `value` is no longer exposed

Primitives no longer have a `value` that you could access and modify. We had previously removed that from the documentation and it is now removed from the code. You can just `get()` and `set()` as you would any other observable. It turned out to cause more bugs than it was worth and made the TypeScript types overly complex.

### Removed get(false)

Use `peek()` instead.

## 0.17 to 0.18

The tracing functions are renamed to use\* to be inline with hooks:

- useTraceListeners
- useTraceUpdates
- useVerifyNotTracking
- useVerifyOneRender (new)

## 0.16 to 0.17

### Primitives are now returned as observables

Observables previously tried to be clever by returning primitives directly, which was great in making it easy to work with state directly. But especially as the goal has moved more towards fine-grained reactivity, the balance shifted towards observable objects being better. So accessing primitives through state now returns observables like anything else.

#### Raw primitives:

- Pro: Easy to work with
- Con: Required `obs()` to get the observable to pass to props or render directly
- Con: Easy to track a value without realizing it

#### Observable primitives

- Pro: More consistent
- Pro: Easier to deal with undefined
- Pro: Can dot through undefined paths easily
- Pro: Doesn’t need `obs()` or set by key
- Pro: Easier to use fine-grained features without `obs()` everywhere
- Pro: Easier to pass as props without needing `obs()`
- Con: Requires `get()` for primitives

#### Changes to make:

**get()**

Wherever you were accessing primitives directly, add a `.get()` to the end of it.

**set(key, value)**

Change set by key to access the node first. It will now work fine if the node is undefined.

From: `state.profile.set('name', 'Annyong')`

To:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`state.profile.name.set('Annyong')`

**obs()**

Just remove it. The default behavior is now the same as what `obs()` did before.

### Hooks renamed

`useComputed` is now `useValue`, re-rendering only when the return value changes.

`useComputed` now returns a `computed` observable.

## 0.15 to 0.16

### enableLegendStateReact() to observe, removed observer

Legend-State now automatically tracks observable access in any component. To set it up, just call `enableLegendStateReact()` at the beginning of your app.

Now `observer` is no longer needed, so just remove all usage of `observer`.

## 0.14 to 0.15

### Safety

There are now three levels of safety: **Unsafe**, **Default**, and **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... TypeScript should show errors so it should be easy to find them. Replace those with `.set(...)` or pass in `false` as the second parameter to `observable` to go back to "Unsafe" mode.

```js
// 1. Unsafe: Use false for the previous unsafe behavior
const obs = observable({ ... }, /*safe*/ false)

// 2. Default: The new default behavior prevent directly assigning to objects, but allows directly assining to primitives
const obs = observable({ text: 'hello',  obj: {} })

obs.text = 'hi'
// ✅ Setting a primitive works in default mode but not in safe mode.

obs.obj = {}
// ❌ Error. Cannot assign to objects directly.

// 3. Safe: Safe mode prevents all direct assignment
const obs = observable({ text: 'hello',  obj: {} }, /*safe*/true)

obs.text = 'hi'
// ❌ Error. Cannot assign directly in safe mode.
```

### Renamed ref to obs

`ref` was a bit unclear and conflicted with React - the new feature to [directly render observables](../../react/fine-grained-reactivity#render-an-observableselector-directly) requires a `ref` property. So it is now renamed to `obs`, which feels more intuitive as it is used to get an observable.

```js
const state$ = observable({ text: "" });

// Before
const textRef = state$.ref("text");
const textRef2 = state$.text.ref();

// Now
const textObs = obs.obs("text");
const textObs2 = obs.text.obs();
```

### Array optimizations

The array optimizations are now opt-in, because they are only useful in React and can potentially have some unexpected behavior in React if modifying the DOM externally. You can enable them by using the `For` component with the `optimized` prop. See [Arrays](../../usage/observable#arrays) for more.

```jsx
import { observable } from "@legendapp/state";
import { For, observer } from "@legendapp/state/react";

const list$ = observable({ items: [{ text: "First" }] });

const Row = observer(function Row({ item$ }) {
  return <div>{item$.text.get()}</div>;
});

const List = observer(function List() {
  // The optimized prop enables the optimizations which were previously default
  return <For each={list$.items} item={Row} optimized />;
});
```

### Shallow

Since there's now a additionally the `optimized` tracking for arrays, the shallow option on `get()` and `obs()` now has another option. So instead of passing `shallow` to an observable, use the `Tracking` namespace now.

```js
import { observable, Tracking } from "@legendapp/state";

const obs = observable([]);

// Before
obs.get(shallow);

// Now
obs.get(Tracking.shallow);
```

### Batching

The `observableBatcher` namespace is removed and the batching functions are now exported on their own.

```js
import { batch, beginBatch, endBatch, observable } from '@legendapp/state'

const obs1 = observable(0);
const obs2 = observable(0);

// begin/end
beginBatch()
obs1.set((value) => value + 1)
obs2.set((value) => value + 1)
endBatch()

// batch()
batch(() => {
    obs1.set((value) => value + 1)
    obs2.set((value) => value + 1)
})
```

### Change functions => observe/when

The new `observe` and `when` functions can automatically track all observables accessed while running them. This made the old extra change utilities unnecessary, so `onTrue`, `onHasValue`, `onEquals`, and `onChangeShallow` have been removed, saving 200 bytes (7%) from the bundle size. These are the new equivalents:

```js
import { observe, when, observable } from "@legendapp/state";

const obs = observable({ value: undefined });
const handler = () => {};

// onTrue
obs.value.onTrue(handler);
// New onTrue equivalent
when(() => obs.value === true, handler);

// onHasValue
obs.value.onHasValue("text", handler);
// onHasValue equivalent
when(() => obs.value, handler);

// onEquals
obs.value.onEquals("text", handler);
// onEquals equivalent
when(() => obs.value === "text", handler);

// onChangeShallow
obs.value.onChangeShallow(handler);
// onChangeShallow equivalent
obs.value.onChange(handler, { shallow: true });

// Generic observe example
observe(() => {
  obs.value.get();
});
```

### Primitive current => value

Primitive observables are now wrapped in `{ value }` instead of `{ current }`. You can also now modify the `value` directly.

```js
import { observable } from "@legendapp/state";

const obs = observable(10);
// Before
obs.current === 10;
obs.curent = 20; // ❌ Error
// Now
obs.value === 10;
obs.value = 20; // ✅ Works
```

### Renamed observableComputed and observableEvent

`observableComputed` is now just `computed` and `observableEvent` is now just `event`.

```js
import { computed, event, observableComputed, observableEvent } from "@legendapp/state";

// Before
const legacyValue = observableComputed(() => 0);
// Now
const value = computed(() => legacyValue.get());

// Before
const legacyEvent = observableEvent(() => {});
// Now
const evt = event(() => legacyEvent());
```

### Renamed LS to Bindable

The automatically bound exports are now named better and in their own exports, so change your exports from `LS` to:

```text
// Web
import { Bindable } from "@legendapp/state/react-components";

// React Native
import { Bindable } from "@legendapp/state/react-native-components";
```

### Renamed Isolate to Computed

The control flow component `Isolate` is renamed to `Computed` for naming consistency.

### Removed memo and isolate props

We found these confusing in practice as it wasn't super clear when a component was getting memoized, and it's not much extra work to use the Memo and Computed components directly. If you were using those, switch to the Computed and Memo components instead

```jsx
import { Computed, Memo } from "@legendapp/state/react";

// Before
<div memo>...</div>
<div computed>...</div>

// Now
<Memo><div>...</div></Memo>
<Computed><div>...</div></Computed>
```
