I have an array plan defined using store state management as follows:
import { reactive } from 'vue'
export const store = reactive({
plan: []
})
I want to reset this array before filling it again, I have tried:
import { store } from "./store.js";
store.plan = [];
store.plan = newPlan; // store.plan is accumulated here without reset!
Can you please tell me how can I reset this reactive array before assigning the new array to it?
Actually, you are creating a new instance for the array. As the objective of reactive function is to share the same instance, it keeps the old array.
You should edit the array instance by cleaning it like that:
store.plan.splice(0, store.plan.length)
You can also use store.plan.length = 0.
Related
I have an array declared as
var subjects : string[] = [];
Then set the array with the values as
subjects.push("C")
subjects.push("C++")
Then I want to update this array from useEffect. I tried below code
useEffect(()=>{
subjects.push("Java")
},[])
when displaying subjects array, it is not updating with "Java".
Can anyone help me to solve this?
You are not triggering any re-rendering of your component. Re-renders run when props or states changes. You are trying to update an immutable piece of data in React world instead and the algorithm doesn't know that it changed, the UI doesn't reflect the updated subjects array as consequence.
So you could declare your subjects array in this way using the useState hook:
const [subjects, setSubjects] = useState<string[]>(["C++", "C"])
Then you can update it in this way:
setSubjects((prev) => [...prev, 'Java']);
Use hook useState for creating your array.
const [value, setValue] = useState<string[]>([])
And if you want change array, you should set new value as setValue([...value, "Java"]);
Declare a state hook and store subjects values in it.
const [subjects, setSubjects] = useState<string[]>(["C", "C++"]);
Then in useEffect change the subjects value:
useEffect(()=>{
const currentSubjects = [...subjects];
currentSubjects.push("Java");
setSubjects(currentSubjects);
},[])
because you aren't using state variable(useState) and your subjects array has Java inside but that can't be visible until your component gets re-rendered so in my opinion you should use const [subjects, setSubjects] : string[] = useState<string[]>([])
and in useEffect:-
useEffect(()=>{
setSubjects([...subjects, 'Java']);},[])
I am in the beginning of learning Vue, and having a hard time understanding how to define props etc. using the composition API.
I have a component (ACE Editor), loaded like this:
<v-ace-editor
v-model:value="currentRecord.text"
/>
The v-ace-editor needs to have the model and value loaded like this: v-model:value.
import {computed, ref} from 'vue';
import collect from "collect.js"
const props = defineProps({
records: {
type: Object,
},
})
//Get all records.
let getRecords = () => {
return collect(props.records)
}
//Filter the records using "collect.js"
const getUnlabeledRecords = () => {
return getRecords()
.where('skipped', false)
.where('processed', false);
}
//Assign all unlabeled records on the first load.
let allRecords = ref(getUnlabeledRecords());
//In order to check if "text" is empty - and if not, set a default value, load the first record into a temp. variable.
let first = allRecords.value.first();
if(first){
first.text ??= "";
}
//Load the current record into a ref.
let current = ref(first);
//Finally, compute it.
let currentRecord = computed(() => current.value)
Looking at this, and coming from a backend background, it feels very bloated.
I have tried the following:
let allRecords = ref(getUnlabeledRecords());
let currentRecord = computed(() => allRecords.value.first())
But doing this leads to me not being able to interact with the currentRecord - nor change the allRecords. This means that if for example currentRecord.text is null from the backend, my ace-editor component fails because it expects a value.
Is there another way to load in these variables?
You actually don't have to called .value of a ref when using it in the template.
So you can actually remove the computed part (last line of your ) and change your template to.
<v-ace-editor
v-model="current.text"
/>
Now, assuming you managed v-model correctly in v-ace-editor (if this is your own component), you should have reactivity kept when modifiying current.text from v-ace-editor.
As a side note, computed properties are read-only. You cannot expect a child component to modify its value by passing it with v-model.
However, you should note that updating records prop from parent component will not update current. For this, maybe you want to add a watcher on records.
Also, personal suggestion, but if you only really care about currentRecord in your component and not all records, maybe you should do the filtering from parent component and only pass currentRecord as a prop. Other personal suggestion, you can declare all your variables in your script with const instead of let. const prevent reassignation, but since you work with refs, you never reassign it, but you change its value property.
I have two different files currently (file1.jsx and file2.jsx both in the same directory). file2.jsx depends on the list of file1.jsx, so I want to be able to call the list somehow. This is my current implementation.
file1.jsx:
function file1 {
// this is the list I want to call in file2.jsx
const [currentSelection, setCurrentSelection] = uuseState([]);
// also tried export const [currentSelection, setCurrentSelection] = uuseState([]);
...
}
file2.jsx:
//gave me errors
import { currentSelection } from "file1";
function file2 {
...
}
If I can get some help, I would much appreciate it!
State in react-native is internal data-set which affects the rendering of components .It is private and fully controlled by the component .
However if you want to use value of currentSelection in file2 , you can store the value of currentSelection in some central store like redux and then fetch it in file2.
Firstly, you wrote uuseState which should be useState, second for you solution. There are many ways to store an array (assuming that you what to pass to second screen by initial value of your use state is empty array)
You can pass it by react Navigation's params if you are using it,
You can store it using async storage and call that storage on second screen.
Here's a sandbox reproducing my issue:
https://codesandbox.io/s/ymmyr3o70x?expanddevtools=1&fontsize=14&hidenavigation=1
For reasons I can't understand, when I add product via the form, the product list is only updated once, then never again.
I'm using a custom hook (useObservable) combined with RxJS to manage state. If you check the console logs, the ReplaySubject does emit the expected values. But the useObservable hook is not triggering an update of the DOM.
What am I missing?
The issue is that your addProduct function mutates the old state instead of creating a new one. Yes, you have the observable emit the state again, but since its the same object as before, calling setValue has no effect and so react does not rerender.
The solution to this is to make the state immutable. For example:
import { ReplaySubject } from "rxjs";
let products = {};
export const products$ = new ReplaySubject(1);
export const addProduct = product => {
products = {...products, [product]: product};
products$.next(products);
};
Let's assume we have some array of objects, and these objects never change. For example, that may be search results, received from google maps places api - every result is rather complex object with id, title, address, coordinates, photos and a bunch of other properties and methods.
We want to use vue/vuex to show search results on the map. If some new results are pushed to the store, we want to draw their markers on the map. If some result is deleted, we want to remove its marker. But internally every result never changes.
Is there any way to tell vue to track the array (push, splice, etc), but not to go deeper and do not track any of its element's properties?
For now I can imagine only some ugly data split - keep the array of ids in vue and have separate cache-by-id outside of the store. I'm looking for a more elegant solution (like knockout.js observableArray).
You can use Object.freeze() on those objects. This comes with a (really tiny!) performance hit, but it should be negligible if you don't add hundreds or thousands of objects at once.
edit: Alternatively, you could freeze the array (much better performance) which will make Vue skip "reactifying" its contents.
And when you need to add objects to that array, build a new one to replace the old one with:
state.searchResults = Object.freeze(state.searchResults.concat([item]))
That would be quite cheap even for bigger arrays.
At the second glance data split seems not so ugly solution for this task. All that we need is using getters instead of the raw vuex state. We suppose that incoming results is an array with any objects that have unique id field. Then the solution could look like:
const state = {
ids: []
}
let resultsCache = {};
const getters = {
results: function(state) {
return _.map(state.ids,id => resultsCache[id]);
}
}
const mutations = {
replaceResults: function(state,results) {
const ids = [];
const cache = {};
(results||[]).forEach((r) => {
if (!cache[r.id]) {
cache[r.id] = r;
ids.push(r.id);
}
});
state.ids = ids;
resultsCache = cache;
},
appendResults: function(state,results) {
(results||[]).forEach((r) => {
if (!resultsCache[r.id]) {
resultsCache[r.id] = r;
state.results.push(r.id);
}
});
}
}
export default {
getters,
mutations,
namespaced: true
}
I created a fork out of vue called vue-for-babylonians to restrict reactivity and even permit some object properties to be reactive. Check it out here.
With it, you can tell Vue to not make any objects which are stored in vue or vuex from being reactive. You can also tell Vue to make certain subset of object properties reactive. You’ll find performance improves substantially and you enjoy the convenience of storing and passing large objects as you would normally in vue/vuex.
You can use shallowRef to achieve this.
First import it:
import {shallowRef} from 'vue';
In your mutations you can have a mutation like this:
mutations: {
setMyObject(state, payload) {
state.myObject = shallowRef(payload.value);
},
}
This will track replacing the object, but not changes to the objects properties.
For completeness here is the documentation to shallowRef:
https://v3.vuejs.org/api/refs-api.html#shallowref