the difference between shallowReactive and shallowRef in vue3? what the shallowRef can do the shallowReactive can not do?
shallowReactive is always used for object,why need shallowRef for object.
ref is for tracking a single value whereas reactive is for tracking an object or array. The implication on this on the shallow versions is that shallowRef will only trigger a listener if the value itself changes, and shallowReactive will only trigger on the first level of values (so the direct keys)
here is an example that I hope illustrates the differences between the behaviour.
The reactive change on reactive and shallowReacive can be observed with a watch
ref and shallowRef behave the same way when it comes to using a watch, but the object is reactive on a deep change in the template.
const s1 = Vue.shallowRef({ x: 1, y: { a: 1 } });
const r1 = Vue.ref({ x: 1, y: { a: 1 } });
const s2 = Vue.shallowReactive({ x: 1, y: { a: 1 } });
const r2 = Vue.reactive({ x: 1, y: { a: 1 } });
Vue.watch(r1, () => console.log("ref (obj) changed"));
Vue.watch(s1, () => console.log("shallowRef (obj) changed"));
Vue.watch(s2, () => console.log("shallowReactive changed"));
Vue.watch(r2, () => console.log("reactive changed"));
setTimeout(() => {
console.log("=== update shallow object ===");
r1.value.x = 2;
s1.value.x = 2;
s2.x = 2;
r2.x = 2;
}, 1000);
setTimeout(() => {
console.log("=== update nested y.a values ===");
r1.value.y.a = 2;
s1.value.y.a = 2;
s2.y.a = 2;
r2.y.a = 2;
}, 2000);
var app1 = Vue.createApp({
data() {
return { r1 };
},
}).mount("#app1");
var app2 = Vue.createApp({
data() {
return { s1 };
},
}).mount("#app2");
<script src="https://unpkg.com/vue#3.0.5/dist/vue.global.prod.js"></script>
<div id="app1">
<pre>ref: {{JSON.stringify(r1, null, 0)}}</pre>
</div>
<div id="app2">
<pre>shallow: {{JSON.stringify(s1, null, 0)}}</pre>
</div>
The difference between a shallowRef and shallowReactive is that a shallowReactive will still unwrap refs at the root level, while a shallowRef will not.
const myRef = shallowRef({name: 'Paul', more: {}})
const myReactive = shallowReactive({name: 'Paul', more: {}})
const age = ref(21)
myRef.value.age = age // will not unwrap
myRef.value.more.age = age // will not unwrap
myReactive.age = age // WILL unwrap
myReactive.more.age = age // will not unwrap
console.log(myRef.value.age.value) // value is not unwrapped
console.log(myRef.value.more.age.value) // value is not unwrapped
console.log(myReactive.age) // value IS unwrapped (root level reactive property)
console.log(myReactive.more.age.value) // // value is not unwrapped
Additional Information For The Curious
Part 1: Simple Difference Between Ref and Reactive
The main differences between ref and reactive in Vue are
A ref can be replaced by another value.
A ref can hold a primitive type.
ref values must be accessed using their value property.
Under the hood, a ref is just a box that holds a reactive value. Because of this box, the value needs to be accessed via its 'value' property. This box allows the entire value to be replaced.
Part 2: What Is Shallow
By default, both refs and reactive will not contain any nested refs. Any refs added to them will be unwrapped; Added refs will still remain reactive because as stated, they will still be a part of a parent reactive value, whether it is a ref or reactive value.
A shallowRef or shallowReactive will not unwrap nested refs. But as stated, shallowReactive will still unwrap top level refs.
Related
How i can write generic function, which take Array of Objects (any type of Object, possible even null and undefined), and filter it to return just valid items of array? If i write it lite this, i will lose genericity :/
// #flow
// Types
type Person = {
id: string,
name: string,
};
type Car = {
id: string,
color: string,
};
// Function definition
const isNotUndefinedOrNull = item => !(item === null || item === undefined);
export const trimList = (list: Array<any> | $ReadOnlyArray<any>): Array<any> => {
return list.filter(isNotUndefinedOrNull);
};
// Constants
const persons = [{ id: 'p1', name: 'Johny' }, null, undefined];
const cars = [{ id: 'c1', color: 'red' }, null, undefined];
// Calls
const trimmedPersons = trimList(persons);
const trimmedCars = trimList(cars);
PROBLEM is, there i have trimmed cars and persons, but flow doesnt know, there is Cars in the trimmedCars list and neither know there is Persons in trimmedPersons list. Flow see just Array and i dont know, how to write is right, to not lose this info.
Flow try
As flow has a bug with Refine array types using filter we use explicit type casting ((res): any): T[]).
function filterNullable<T>(items: (?T)[]): T[] {
const res = items.filter(item => !(item === null || item === undefined);
return ((res): any): T[]);
}
// Example
const a: number[] = filterNullable([1, 2, null, undefined]);
i found it :)
export function trimList<V>(list: Array<?V> | $ReadOnlyArray<?V>): Array<V> {
return R.filter(isNotUndefinedOrNull, list);
}
So I'm trying to change the order of an array using redux and immutable. What I'm basically doing is grabbing the current array, modifying it, and updating the state with modified copy, like so:
case MOVE_LAYOUT: {
const index = action.payload;
const lower = index - 1;
const element = state.getIn(['someArray', index]);
if (action.direction === 'up') {
const arr = state.get('someArray');
const newArr = arr.splice(lower, 0, element);
const remove = newArr.splice(index, 1);
return state.set('someArray', remove);
}
return false;
}
For the sake of the question, let's assume action.payload is passed properly, and that a console.log(remove.toJS()) will return what I want. However, when I update someArray nothing happens. Why would this be?
So here's the answer:
case MOVE_LAYOUT: {
const index = action.payload;
const arr = state.get('someArray').toJS();
let next;
if (action.direction === 'up') {
next = index - 1;
} else {
next = index + 1;
}
const temp = arr[index];
arr[index] = arr[next];
arr[next] = temp;
return state.set('someArray', fromJS(arr));
}
Basically, I'll make a copy of the array, and modify the index (move by 1 up or 1 down), and set the modified array copy back to state. Problem with initial approach was not using .toJS() and .fromJS(), so the results were immutable list, rather than JS object.
How to set a property to value that should be resolve.. like this one..
const getDataFromServer = (id) => ({id: * 2})
R.set(payloadProp, getDataFromServer)({id: 4}); // WRONG, is setting to a function instend to resolve a function with `{id: 4}`
const fetch = (id) => {
return { num: id * 2 }
};
const idProp = R.lensProp('id');
const getDataFromServer = R.pipe(R.view(idProp), fetch);
const payloadProp = R.lensProp('payload');
const setPayloadFromFetch = R.set(payloadProp, getDataFromServer); // NOT WORK, return payload as function
const obj = { id: 1, payload: { message: 'request' } }
const ret = setPayloadFromFetch(obj);
console.log(ret);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
The problem is that R.set takes a value, not a function, for its second parameter. And you can't switch to R.over, which does take function but calls it with the current value at that lens, not the full data object supplied to the outer function.
The simplest way to solve this is simply to pass the object in both places:
const setPayloadFromFetch = obj => R.set(payloadProp, getDataFromServer(obj), obj);
But if you're intent on making this point-free, lift is your friend:
const setPayloadFromFetch = R.lift(R.set(payloadProp))(getDataFromServer, identity);
although you could also use R.ap, which would be very nice except that R.set takes its parameters in the wrong order for it, and so you have to use R.flip
const setPayloadFromFetch = R.ap(R.flip(R.set(payloadProp)), getDataFromServer);
You can see all these in the Ramda REPL.
I'm new to flow, any trying to cover some of my functions, however often I have these snippets where I extract fields form an object based on some condition. But I'm struggling to cover them with flow.
const _join = function ( that: Array<Object>, by: string, index: number) {
that.forEach((thatOBJ: {[string]: any}, i: number)=>{
let obj: {[string]: any} = {};
for (let field: string in thatOBJ) {
if (field !== by) {
obj[`${index.toString()}_${field}`] = thatOBJ[field]; // NOT COVERED
} else {
obj[field] = thatOBJ[field]; // NOT COVERED
}
that[i] = obj;
}
});
}
The array that in this code is a data array so can really be in any format of mongodb data.
Any ideas on what to add to make the two lines which are not covered by flow covered?
Thanks.
A few notes...
This function has a "side effect" since you're mutating that rather than using a transformation and returning a new object.
Array<Object> is an Array of any, bounded by {}. There are no other guarantees.
If you care about modeling this functionality and statically typing them, you need to use unions (or |) to enumerate all the value possibilities.
It's not currently possible to model computed map keys in flow.
This is how I'd re-write your join function:
// #flow
function createIndexObject<T>(obj: { [string]: T }, by: string, index: number): { [string]: T } {
return Object.keys(obj).reduce((newObj, key) => {
if (key !== by) {
newObj[`${index}_${key}`] = newObj[key]
} else {
newObj[key] = obj[key]
}
return newObj
}, {})
}
// NO ERROR
const test1: { [string]: string | number } = createIndexObject({ foo: '', bar: 3 }, 'foo', 1)
// ERROR
const test2: { [string]: string | boolean } = createIndexObject({ foo: '', bar: 3 }, 'foo', 1)
I guess my question could also summed up as something like
Is there an idiomatic ES6 way to have:
array.map(identity) === array ?
array.filter(i => true) === array ?
{obj..., attr: obj.attr} === obj ?
I know, it has not been implemented like that in ES6, but is there some possible syntax I'm missing or simple helper functions to have these properties true without resorting to an immutable lib?
I use Babel and new JS features, with immutable js objects.
I would like to know how to make my reducers more efficient and generate less unnecessary object copies
I'm not interested in a lib (Mori/ImmutableJS) solution.
I have a reducer that manages a paginated list.
The pages attribute is actually an Array[Array[item]]
Here is my reducer:
const initialState = {
isLoading: false,
pages: [],
allStamplesLoaded: false
};
function reducer(state = initialState, event) {
switch (event.name) {
case Names.STAMPLE_DELETED:
return {
...state,
pages: removeStampleFromPages(state.pages,event.data.stampleId)
};
case Names.STAMPLE_UPDATED:
return {
...state,
pages: updateStampleInPages(state.pages,event.data.apiStample)
};
case Names.STAMPLE_PAGES_CLEANED:
return {
...initialState,
};
case Names.STAMPLE_PAGE_REQUESTED:
return {
...state,
isLoading: true
};
case Names.STAMPLE_PAGE_LOADED:
const {stamplePage,isLastPage} = event.data;
return {
...state,
isLoading: false,
pages: [...state.pages, stamplePage],
isLastPage: isLastPage
};
case Names.STAMPLE_PAGE_ERROR:
return {
...state,
isLoading: false
};
default:
return state;
}
}
I also have these helper functions:
function removeStampleFromPages(pages,deletedStampleId) {
return pages.map(page => {
return page.filter(apiStample => apiStample != deletedStampleId)
})
}
function updateStampleInPages(pages,newApiStample) {
return pages.map(page => {
return updateStampleInPage(page,newApiStample);
})
}
function updateStampleInPage(page,newApiStample) {
return page.map(apiStample => {
if (apiStample.id === newApiStample.id) {
return newApiStample;
}
else {
return apiStample;
}
})
}
As you can notice, everytime an event such as STAMPLE_UPDATED is fired, then my reducer always return a new state, with a new array of array of pages, even if none of the items of the array were actually updated. This creates unnecessary object copying and GC.
I don't wan to optimize this prematurely nor introduce an immutable library in my app, but I'd like to know if there are any idiomatic ES6 ways to solve this problem?
Immutable data structures such as Immutable.js and Mori use a clever trick to avoid recreating the whole structure all the time.
The strategy is fairly simple: when you update a property drill down to the property, change it and rewrap all the property from this node till the root.
Let's assume you want to change the property c to 4 in the following state:
const state1 = {
a: {
b: {
c: 1
},
d: [2, 3, 4],
e: 'Hello'
}
}
The first step is to update c to 4. After that you need to create
a new object for b (because c changed)
a new object for a (because b changed)
and new object for the state (because a changed).
Your new state will look like this (a * next to an object means the object has been recreated)
const state2 = *{
a: *{
b: *{
c: 4
},
d: [2, 3, 4],
e: 'Hello'
}
}
Notice how d and e have not been touched.
You can now verify things are properly working:
state1 === state2 // false
state1.a === state2.a // false
state1.a.b === state2.a.b //false
state1.d === state2.d // true
state1.e === state2.e // true
You may notice that d and e are shared between state1 and state2.
You could use a similar strategy to share information in your state without recreating a whole new state all the time.
As for your initial question:
array.map(identity) !== array
array.filter(i => true) !== array
{obj..., attr: obj.attr} !== obj
the answer is very simple.
When an array or an object is created, the Javascript VM assigns internally an identifier to that object. The identifier is incremental, so no two arrays/objects are alike.
When you perform an identity check on arrays or objects, only the internal identifier is checked for a match.
a = [] // internal identifier 1
[] // internal identifier to 2
b = [] // internal identifier 3
a === b // id 1 === id 3 is FALSE!
a === a // id 1 === id 1 is TRUE!