State

How to store information in components and refresh them?


For storing information in component use one of these functions:

  • ref
  • reactive
  • computed
  • readonly

For storing information that are reactive only root level properties use one of these functions:

  • shallowRef
  • shallowReactive
  • shallowReadonly

ref()

Takes an inner value and returns a reactive and mutable ref object, which has a single property .value that points to the inner value.

ref method is used to convert non-object variables into objects and then to grant reactive. ref is pretty much the reactive that works also with primitives.

The ref object is mutable.

  • you can assign new values to .value - it is also reactive
  • any read operations to .value are tracked, and write operations will trigger associated effects.

If an object is assigned as a ref's value, the object is made deeply reactive with reactive(). This also means if the object contains nested refs, they will be deeply unwrapped.

const count = ref(0);
count.value++;
console.log(count.value); // 1

reactive()

Not finished

https://stackoverflow.com/questions/66585688/what-is-the-difference-between-ref-toref-and-torefs

The reactive conversion affects all nested properties. A reactive object also deeply unwraps any properties that are refs while maintaining reactivity.

If your original variable is already an object (or array), a ref wrapping is not needed because it is already a reference type. It only needs Vue's reactive functionality.

const obj = reactive({ count: 0 });
obj.count++;
console.log(obj.value.count); // 1
const count = ref(1);
const obj = reactive({ count });

// ref will be unwrapped
console.log(obj.count === count.value); // true

// it will update `obj.count`
count.value++;
console.log(count.value); // 2
console.log(obj.count); // 2

// it will also update `count` ref
obj.count++;
console.log(obj.count); // 3
console.log(count.value); // 3

Note that refs are not unwrapped when accessed as array or collection elements:

const books = reactive([ref("Vue 3 Guide")]);
// need .value here
console.log(books[0].value);

const map = reactive(new Map([["count", ref(0)]]));
// need .value here
console.log(map.get("count").value);

When assigning a ref to a reactive property, that ref will also be automatically unwrapped:

const count = ref(1);
const obj = reactive({});

obj.count = count;

console.log(obj.count); // 1
console.log(obj.count === count.value); // true

edge cases to watch

  1. the recursive creation of nested proxies can only happen if there is a nested object. If a given property does not exist, or it exists but it is not an object, no proxy can be created at that property. E.g. this is why reactivity does not work off the baz variable created by const baz = state.baz, nor the bar variable of const bar = state.foo.bar. To make it clear, what it means is that you can use state.baz and state.foo.bar in your template/computed/watch, but not baz or bar created above.

  2. if you extract a nest proxy out to a variable, it is detached from its original parent. This can be made clearer with an example. The second assignment below (state.foo = {bar: 3}) does not destroy the reactivity of foo, but state.foo will be a new proxy object while the foo variable still points the to original proxy object.

const state = reactive({ foo: { bar: 1 } });
const foo = state.foo;

state.foo.bar = 2;
foo.bar === 2; // true, because foo and state.foo are the same

state.foo = { bar: 3 };
foo.bar === 3; // false, foo.bar will still be 2

computed()

readonly()

Takes an object (reactive or plain) or a ref and returns a readonly proxy to the original.

Any nested property accessed will be readonly as well. It also has the same ref-unwrapping behavior as reactive(), except the unwrapped values will also be made readonly.

const original = reactive({ count: 0 });

const copy = readonly(original);

watchEffect(() => {
  // works for reactivity tracking
  console.log(copy.count);
});

// mutating original will trigger watchers relying on the copy
original.count++;

// mutating the copy will fail and result in a warning
copy.count++; // warning!

shallowRef()

Shallow version of ref().

const state = shallowRef({ count: 1 });

// does NOT trigger change
state.value.count = 2;

// does trigger change
state.value = { count: 2 };

shallowRef() is typically used for performance optimizations of large data structures, or integration with external state management systems.

shallowReactive()

Shallow version of reactive().

Unlike reactive(), there is no deep conversion: only root-level properties are reactive for a shallow reactive object. Property values are stored and exposed as-is - this also means properties with ref values will not be automatically unwrapped.

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2,
  },
});

// mutating state's own properties is reactive
state.foo++;

// ...but does not convert nested objects
isReactive(state.nested); // false

// NOT reactive
state.nested.bar++;

Shallow data structures should only be used for root level state in a component. Avoid nesting it inside a deep reactive objects.

shallowReadonly()

Shallow version of readonly().

const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2,
  },
});

// mutating state's own properties will fail
state.foo++;

// ...but works on nested objects
isReadonly(state.nested); // false

// works
state.nested.bar++;

Shallow data structures should only be used for root level state in a component.