Storing JSON structure in VueX - javascript

I'm fairly new to Vue/VueX, and I'm researching a solution to store JSONs in the VueX state. At first it seems pretty straightforward:
state {
jsonthing: { ... }
}
The problem: getters return Observer type, not Object type. So I can do this and retrieve the entire JSON structure:
getters: {
getJSON(state) {
return state.jsonthing;
}
}
But I can't retrieve a node or a single value of the JSON, like this:
getters: {
getOneNode: state =>
nodeName => {
return state.jsonthing[nodeName];
}
}
}
The getter retrieves state.jsonthing as an Observer. I can't find a way to extract the desired content of the JSON from this Observer and return that. I know I can do it in my components using mapState but that's not what I'm looking for. Is there a way to do it in the getter?
What I'm currently doing is that I store the JSON as a string (JSON.stringify()) and convert it back in the getter (JSON.parse()). Strings are retrieved as strings, and not Observers. It works, but it's a hack.
While we are here, I also can't find any documentation for the Observer type. I'd appreciate if someone could drop a URL!

All right guys, you're not very active today, so here is a solution. If you know a better one, please let me know.
Solution 1:
Convert the JSON into a string with JSON.stringify() and store it as a string. Then the getter converts it back with JSON.parse(). This works, but admittedly ugly.
Solution 2:
Generate a function that returns the JSON. This is way better.
state: {
jsonthing: null
}
mutations: {
INITIALIZE(state, jsonthing) {
state.jsonthing = new Function(`return ${ JSON.stringify(jsonthing) }`)
}
}
getters: {
getOneNode: state =>
nodeName => {
return state.jsonthing()[nodeName];
}
}
}
I've written an article on Medium about this method and a practical application (which I'm actually implementing).
https://medium.com/developer-rants/simple-dynamic-localization-in-vue-with-vuex-b429c525cd90

Related

Redux data not rerendering when setting value within object (Immutable.js)

I have an immutable.js Map stored in Redux that is structured like:
reduxObject: {
details: {}
...
objectToChange: {
myPosts: [{
name: 'someName',
link: 'someLink',
}],
drafts: []
}
}
I am trying to append the array objectToChange.myPosts in a reducer function using
let temp = state.getIn([objectToChange, myPosts])
temp.push(action.payloadData)
return state.setIn([objectToChange, myPosts], temp)
The redux data is getting updated, however the displayed redux data isn't getting rerendered. I was expecting the state.setIn to create a new immutable object causing react native to trigger a rerender. Any suggestions or help would be greatly appreciated. Thanks ahead of time
So I found a workaround to using immutable that i'm not sure is acceptable but it works. I am now using lodash's cloneDeep function.
let temp = _.cloneDeep(state)
temp[action.payloadType][action.payloadLoc].push(action.payloadData)
return (temp)
Ok so cloneDeep was a bad workaround but i found the proper solution to the problem. I needed to return the statement like this:
case 'postNewItemFbData':
let tempUpdate = state[action.payloadType][action.payloadLoc]
tempUpdate.push(action.payloadData)
return {
...state,
[`${action.payloadType}.${action.payloadLoc}`]: tempUpdate
}

Vue Array converted to Proxy object

I'm new to Vue. While making this component I got stuck here.
I'm making an AJAX request to an API that returns an array using this code:
<script>
import axios from 'axios';
export default {
data() {
return {
tickets: [],
};
},
methods: {
getTickets() {
axios.get(url)
.then((response) => {
console.log(response.data) //[{}, {}, {}]
this.tickets = [...response.data]
console.log(this.tickets) //proxy object
})
},
},
created() {
this.getTickets();
}
};
</script>
The problem is, this.tickets gets set to a Proxy object instead of the Array I'm getting from the API.
What am I doing wrong here?
Items in data like your tickets are made into observable objects. This is to allow reactivity (automatically re-rendering the UI and other features). This is expected and the returned object should behave just like the array.
Check out the reactivity docs because you need to interact with arrays in a specific pattern or it will not update on the ui: https://v3.vuejs.org/guide/reactivity-fundamentals.html
If you do not want to have reactivity - maybe you never update tickets on the client and just want to display them - you can use Object.freeze() on response.data;
if you want reactive information use toRaw
https://vuejs.org/api/reactivity-advanced.html#toraw
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
or use unref if you donot want ref wrapper around your info
https://vuejs.org/api/reactivity-utilities.html#unref
You can retrieve the Array response object from the returned Proxy by converting it to a JSON string and back into an Array like so:
console.log(JSON.parse(JSON.stringify(this.tickets)));
You're not doing anything wrong. You're just finding out some of the intricacies of using vue 3.
Mostly you can work with the proxied array-object just like you would with the original. However the docs do state:
The use of Proxy does introduce a new caveat to be aware of: the proxied object is not equal to the original object in terms of identity comparison (===).
Other operations that rely on strict equality comparisons can also be impacted, such as .includes() or .indexOf().
The advice in docs doesn't quite cover these cases yet. I found I could get .includes() to work when checking against Object.values(array). (thanks to #adamStarrh in the comments).
import { isProxy, toRaw } from 'vue';
let rawData = someData;
if (isProxy(someData)){
rawData = toRaw(someData)
}

Filtering a JSON response with Vue

I'm practicing using axios with Vue, but I think this may be more of a general JSON question.
I've successfully used axios to get my local products.json file and I'm then using filter to create a new array that only has products that have a matching department property, and looping those out.
Is this the correct way of doing this, or can I actually filter the JSON result on the original axios call? I understand I can to pass a parameter which will in turn perform a specific database call, and only provide the required JSON in the first place.
data(){
return {
products: []
}
},
components: {
Product
},
computed: {
foodProducts(){
return this.products.filter(x => x.department == 'Food')
}
},
mounted() {
axios
.get('./json/products.json')
.then(response => (this.products = response.data.products))
}
Thanks. Just trying to clarify the theory behind it.
It works in many ways depending on your situation or requirement.
Your way works. Alternatively, you can also filter the result directly from the API call assuming that the backend is returning a full result.
data() {
return {
filteredProducts: []
}
}
mounted() {
axios.get(API_URL)
.then(response => {
const products = response.data
this.filteredProducts = products.filter(product => product.department.includes('food'))
})
}
If you're querying the products list from a Back-end server,
you may use query parameters like
xxx/products?department=XXX
then the backend server can do the filtering for you.
In your case, it looks like you are simply reading a local JSON file, so the entire JSON is returned, and you have to filter yourself.

Vue.js - Add or set new data to array in store

I have two Vue components that use a common array set in a store like this:
var store = {
state: {
myArray: []
}
};
var FirstComp = Vue.extend({
template: '#first-template',
data: function () {
return {
arrayData: store.state.myArray
};
}
});
/* A second component quite identical */
I followed the example given in the Vue js guide.
I'm trying to update the data in the array in the store with new data from another array (after an ajax call), so that it impacts both components. I would like to have a nice way of replacing / concating the old array with a new one. I know I can't just replace the array like this store.state.myArray = newArrayData;because I would loose the Vue binding. But the method given in the docs (at least for concat) doesn't work in the case of the store (or maybe I'm missing something?).
Right now, the only way I've found is to use a foreach with push, $removeor $set depending on the operation and it is not that elegant and practical.
For example, for concat, I do this:
$.each(newArray, function (i, val) {
store.state.myArray.push(val);
});
But for replacing it gets uglier. What would be the proper way to this?
(For info, I'm not using Vuex for state management and I don't plan to at the moment, I'm keeping it very simple)
To make the assignment work you can just use "state" in your component like this:
var FirstComp = Vue.extend({
template: '#first-template',
data: function () {
return {
state: store.state
};
}
});
And then use state.myArray. This way if you will do store.state.myArray = newValue it won't break the binding.

From Event driven OO to Redux architecture

I have a structure that is pretty much OO and I am about to migrate to React/Redux due to event mess.
I am curious what to do with current modules, I have objects that have schema like:
User {
getName()
getSurname()
etc...
}
And there are lots of these, they were used as fasade/factories for raw json data as I used to pass json and manipulate it (mutable data)
Now how to solve this in redux?
I get to the part where I have an async action call, I recieve raw data from api and than what?
Should I pass 'complex' object with their getters/setters to state? Its said to be immutable so it doesnt seem well with redux recomendations.
Or maybe convert the class-like elements to accessors like:
function getName(rawJson) {
return rawJson.name
}
function setName(rawJson, name) {
return Object.assign({}, rawJson, {name})
}
parse it in action and return a rawJSON chunk from action to reducer and than stick it to the new state?
EDIT:
A simple pseudocode module for user:
function User(raw) {
return {
getName: function() {
return raw.name
}
setName: function(name) {
raw.name = name
return this
}
}
}
My point is about moving all data and flattening/normalizing it in store - would it be fine to have an array of e.g. User objects in store? or should they all be pure json. I want to be sure its really only correct way to have all those objects turn into basic values cause it gonna be lots of work.
Not sure if this will be totally relevant, but if I'm understanding your question and putting it another way: where to put business logic and other validations/manipulations:
https://github.com/reactjs/redux/issues/1165
I personally follow this trend as well in that I put all of my async action manipulation in my action creators before storing them in a format of my choosing in the reducer.
Here, I choose to convert to JSON whatever objects I get back from the API. Similarly you can do whatever logic you need here before dispatching a success request to store in your reducers.
Hope that helps/is relevant to whatever you were asking...

Categories

Resources