I'm trying to figure out the best way for my Redux Store to handle lists. Right now it looks like this:
Store = {
users: [],
posts: [],
lists: [],
}
My problem with this, is the list array. Essentially it's a store for paginated lists of a specific resource, so for example:
lists: [
{
id: 'users/43/posts',
items: [25, 36, 21]
}
]
Since I am using the url as the id, my component that shows a user's list of posts will know exactly which list to display. Now someone has told me that, this is a very very bad idea. And I just want some advice on what could be better. Another approach suggested was this:
users: [{
id: 2,
posts: [
{
url: 'users/2/posts',
items: [13, 52, 26],
}
]
}]
So what I don't understand, how does Redux know where to save this list? Do I have to specify in the action arguments where to save it?
Thank you for your advice.
Well, technically, anything works if you make it work! The second approach looks more mature, though. You don't want to use URLs as ID. IDs should be numbers or special sequence of characters+numbers. When your application grows, you'll want to normalize your data i.e. store the IDs in a separate array and transform the array of objects into an object with keys as ID.
Example from Normalizr
[{
id: 1,
title: 'Some Article',
author: {
id: 1,
name: 'Dan'
}
}, {
id: 2,
title: 'Other Article',
author: {
id: 1,
name: 'Dan'
}
}]
can be normalized to -
{
result: [1, 2],
entities: {
articles: {
1: {
id: 1,
title: 'Some Article',
author: 1
},
2: {
id: 2,
title: 'Other Article',
author: 1
}
}
}
}
When your application grows, you'll have multiple reducers and sub-reducers. You'll want to slice a specific portion of your state-tree and so on. For that reason someone might have advised you to store your state in a different manner.
But again, anything works if you make it work! Good luck!
Related
I'm trying to understand why Javascript array sort doesn't work with the following logic. I have no problems making my own algorithm to sort this array, but I'm trying to make it with the Javascript sort built-in method to understand it better.
In this code, I want to push entities that "belongs to" another entity to the bottom, so entities that "has" other entities appear on the top. But apparently, the sort method doesn't compare all elements with each other, so the logic doesn't work properly.
Am I doing something wrong, or it is the correct behavior for the Javascript sort method?
The code I'm trying to execute:
let entities = [
{
name: 'Permission2',
belongsTo: ['Role']
},
{
name: 'Another',
belongsTo: ['User']
},
{
name: 'User',
belongsTo: ['Role', 'Permission2']
},
{
name: 'Teste',
belongsTo: ['User']
},
{
name: 'Role',
belongsTo: ['Other']
},
{
name: 'Other',
belongsTo: []
},
{
name: 'Permission',
belongsTo: ['Role']
},
{
name: 'Test',
belongsTo: []
},
]
// Order needs to be Permission,
let sorted = entities.sort((first, second) => {
let firstBelongsToSecond = first.belongsTo.includes(second.name),
secondBelongsToFirst = second.belongsTo.includes(first.name)
if(firstBelongsToSecond) return 1
if(secondBelongsToFirst) return -1
return 0
})
console.log(sorted.map(item => item.name))
As you can see, "Role" needs to appear before "User", "Other" before "Role", etc, but it doesn't work.
Thanks for your help! Cheers
You're running into literally how sorting is supposed to work: sort compares two elements at a time, so let's just take some (virtual) pen and paper and write out what your code is supposed to do.
If we use the simplest array with just User and Role, things work fine, so let's reduce your entities to a three element array that doesn't do what you thought it was supposed to do:
let entities = [
{
name: 'User',
belongsTo: ['Role', 'Permission2']
},
{
name: 'Test',
belongsTo: []
},
{
name: 'Role',
belongsTo: ['Other']
}
]
This will yield {User, Test, Role} when sorted, because it should... so let's see why it should:
pick elements [0] and [1] from [user, test, role] for comparison
compare(user, test)
user does not belong to test
test does not belong to user
per your code: return 0, i.e. don't change the ordering
we slide the compare window over to [1] and [2]
compare(test, role)
test does not belong to role
role does not belong to test
per your code: return 0, i.e. don't change the ordering
we slide the compare window over to [2] and [3]
there is no [3], we're done
The sorted result is {user, test, role}, because nothing got reordered
So the "bug" is thinking that sort compares everything-to-everything: as User and Role are not adjacent elements, they will never get compared to each other. Only adjacent elements get compared.
Lets say I have the following list in data:
data: {
todos: [
{ id: 1, title: "Learn Python" },
{ id: 2, title: "Learn JS" },
{ id: 3, title: "Create WebApp" }
]
}
Now I want to pass only the entry with id of 2 to the prop:
<dynamic-prop :id=todos[2] :title="todos.title"> </dynamic-prop>
Is something like that possible in Vue?
Sure, you can pass any data on. Just don't forget to add quotation marks and mind the off-by-one problem. So if you want to pass the second element in a (zero-indexed) array, you'd write something like:
<dynamic-prop :id="todos[1].id" :title="todos[1].title"> </dynamic-prop>
Our GraphQL server responds to a query with data that includes an array of objects each of which shares the same id and different values for a different key. For instance, we might have an array that looks like:
[
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 6 },
{ id: 123, name: 'foo', type: 'bar', cost: 7 },
{ id: 123, name: 'foo', type: 'bar', cost: 8 }
]
We can see in the Network tab that the response from the server has the correct data in it. However, by the time it goes through processing by the Apollo Client module the array has been transformed into something that might look like this:
[
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 5 }
]
Essentially what we're seeing is that if all of the objects in an array share the same value for id then all objects in the array become copies of the first object in the array.
Is this the intended behavior of Apollo Client? We thought maybe it had something to do with incorrect caching, but we were also wondering if maybe Apollo Client assumed that subsequent array members with the same id were the same object.
It looks like this is behavior as intended. The Apollo Client normalizes on id.
As the other answer suggests this happens because Apollo normalises by ID. There's a very extensive article on the official blog that explains the rationale of it, along with the underlying mechanisms.
In short, as seen by Apollo's cache, your array of objects contains 4 instances of the same Object (id 123). Same ID, same object.
This is a fair assumption on Apollo's side, but not so much in your case.
You have to explicitly tell Apollo that these are indeed 4 different items that should be treated differently.
In the past we used dataIdFromObject, and you can see an example here.
Today, you would use typePolicies and keyfields:
const cache = new InMemoryCache({
typePolicies: {
YourItem: {
// Combine the fields that make your item unique
keyFields: ['id', 'cost'],
}
},
});
Docs
It works for me:
const cache: InMemoryCache = new InMemoryCache({ dataIdFromObject: o => false )};
previous answer solves this problem too!
Also you can change the key name(for example id => itemId) on back-end side and there won't be any issue!
I have the same issue. My solution is to set fetchPolicy: "no-cache" just for this single API so you don't have to change the InMemoryCache.
Note that setting fetchPolicy to network-only is insufficient because it still uses the cache.
fetchPolicy document
I am working on a application which is nicely modularized using requirejs. One of the modules called data service is in charge of providing other modules with data. Pretty much all get* methods of this module return javascript script objects in the the following format:
res = {
totalRows: 537,
pageSize: 10,
page: 15,
rows: [
{
id: 1,
name: 'Angelina'
...
},
{
id: 2,
name: 'Halle'
...
},
{
id: 3,
name: 'Scarlet'
...
},
{
id: 4,
name: 'Rihanna'
...
},
{
id: 5,
name: 'Shakira'
...
},
....
//10 rows
{
id: 10,
name: 'Kate'
...
}
]
}
Is it possible to initialize the data table by providing it with rows for the current page, current page number, page size and the total number of records or pages so that it "knows" which page is currently being displayed as well as the number of available pages. Which in turn would allow the DT to build the pager correctly allowing the user to navigate to other pages in which case we would make another call to data service module to retrieve data from the database for the selected page.
I struggled for hours trying to get this to work using Ember-Data with no result, so thought I give Ember-Model a try; still no joy.
I have the following:
App.Item = Ember.Model.extend({
itemName: Ember.attr(),
});
App.Strat = Ember.Model.extend({
stratName: Ember.attr(),
items: Ember.hasMany('App.Item',{key: 'items', embedded: true});
App.Strat.adapter = Ember.FixtureAdapter.create();
App.Strat.FIXTURES =
[
{id: 1, stratName: 's1', items:
[{id: 1, itemName: 'I1'}]},
{id: 2, stratName: 's2', items:
[{id: 2, itemName: 'I2'},
{id: 3, itemName: 'l3'}]}
];
Everything seemed to work fine up to this point. I'm able to display the fixture data via the templates. What I want to do is to allow the user to add additional strat records, by showing a pre-populated strat record on the screen, allowing users to make modifications, then save it with the two strat records loaded from the fixture data. I tried the following:
var dummyStrat =
{id: 100, stratName: "s5", items:
[{id: 101, itemName: "I5", strategy: 100}]};
var newStrat = App.Strat.create (dummyStrat);
newStrat.save();
This generated the following error:
TypeError: this.get(key).toJSON is not a function.
But no error if I did this:
var dummyStrat =
{id: 100, stratName: "s5"};
var newStrat = App.Strat.create (dummyStrat);
newStrat.save();
What am I doing wrong?
What happens if you remove strategy: 100 from the create call? I have pretty much the same code in my app except I'm not setting any variable that isn't already an attribute in my model.
My other suggestion would be to try renaming the items key to stratItems so that the key doesn't have the same name as the computed hasMany attribute.