yury-dymov / redux-object Goto Github PK
View Code? Open in Web Editor NEWBuilds complex JS object from normalized redux store. Best works with json-api-normalizer
License: MIT License
Builds complex JS object from normalized redux store. Best works with json-api-normalizer
License: MIT License
Hello Yury.
I appreciate using your lib. But I have some feature requests.
Please add types for the ability to work with TypeScript.
First off thanks for the great library, it's been working great for us in tandem with json-api-normalizer
. Only issue for us is that we'd like to be able to have resources live under reducers responsible for different domains.
It's simple enough for us to do API requests, normalize them with json-api-normalizer
and then update different reducer's state from a single response action, picking the relevant parts of the normalized data. The issue is the build
function expects all relationships to live on the same reducer as the initial resource. It would be fairly trivial to add additional options to the build function so that relationships could be pulled from different reducers.
Are you open to this idea? If so, we could probably work on a PR if you had guidance on how you'd best like the arguments passed? Our thought was as part of the options a map could be passed like so:
Where the foo
resource has a relationship with type bar
at a different reducer:
const opts = {
relationshipReducers: {
bar: barReducer
}
}
build(fooReducer, 'foo', null, opts);
Then inside build something like this could work:
const relationshipReducer = opts.relationshipReducers[relationship] || reducer;
ret[relationship] = buildRelationship(relationshipReducer, target, relationship, options, cache);
Why does build
return an empty array for relationships that have no data
? It makes it impossible to determine from a built object if the relationship data was present and indeed empty, versus when the relationship data was not present at all.
I think this line should return either null
or undefined
, perhaps most preferably the latter.
Update:
To be more precise, this only occurs when the relationship has links
and no data
, and ignoreLinks: true
is given in the options. In this case, the error is not thrown, but the buildRelationship
function returns []
. I think the fix should be to return undefined
if the error is not thrown. I can make a PR for this case.
According to the spec, resource object can have meta information.
It doesn't look like to be supported at the moment
(related to: yury-dymov/json-api-normalizer#19)
Basically, I am interested in using this in Vuex and Vue, however when Vue components read the store data in to localized computed properties, they take the raw object value at that time and don't take the lazy loader getter. Since Vue is basically caching that result, if I try to access the relationship on the computed property result it comes back as "undefined".
I can think of hacky ways to get around this (ie. touch the relationships within the computed property method just to invoke the loader) however this gets sloppy fast.
So... how about some way to force eager loading of all available relationship data in the local store at build time? Thoughts?
More details here: yury-dymov/json-api-normalizer#2.
Currently, redux-object is a very simple and lightweight implementation. Even redux is not required as redux-object can handle any JS object with a certain structure (json-api-normalizer provides such).
Implementing remote object loading support adds an enormous amount of complexity: fetching remote data taking in account variety of runtime environments and different kinds of authentication, error handling, loading indicators, async code, and so on.
Another important point is that I personally don't need this feature. Generally speaking, I find this approach to be an anti-pattern as it breaks data consistency between client and server โ by the time you lazily fetch nested objects from the server, the original one might be changed in the backend.
I still believe that remote lazy loading for certain cases might be useful and even the only option though. So if it is exactly your case, feel free to share ideas here regarding approaches and APIs, and we might develop it together.
I'm concerned with the inverse operation of this library, which is to convert back an object to its jsonapi form. On its face, it's not that difficult, if there was a way to tell which properties from the object returned by build
were originally relationships, and which ones weren't.
One could assume that properties that return either an array or another object were originally relationships (arrays were one-to-many relationships and objects were one-to-one relationships). However, jsonapi allows for objects to have attributes that return arbitrary json as well. Therefore this solution is not generic enough.
Then I thought about the fact that relationships are encoded in the resulting object as defined properties (if eager
is false
). If there was a way to detect which properties from the object are defined with a getter instead of directly holding the value, that would be great, but I haven't found a way to detect that. And also this would not work when eager
is true
.
Which brings me to my last resort: raising the issue here. Is there today a way to differentiate between the properties of the object returned by build
, and know which ones were relationships in the original jsonapi payload, and which ones weren't? And if not, could this information be encoded somehow in the returned object, and make that a feature of this library?
PS: This is what I'm using now in my project for this purpose, which is far from ideal, but that's what I had to come up with for the short term: https://gist.github.com/gnapse/27655f4dfc1533903f70383f8e145a38
I'm curious how to deal with merging (redux) objects back into the store.
A typical Update action for me involves:
In Step 4. I currently update the 'attributes' hash manually (I use Immutable.js, but that shouldn't matter), but I'm thinking there could be a better way.
I wouldn't mind taking a stab at extending redux-object with a serializeObject (inverse of buildObject) function, if you think it would make sense to include that functionality in the library.
Thoughts?
Hello @yury-dymov
When using your json-api-normalizer
and redux-object
to select data from relationship endpoints like /posts/1/comments
it is not possible to select comments related to this post.
This code won't work if you fetch data from mentioned endpoint.
let post = build(state, 'posts', 1);
let comments = post.comments;
This is because build
builds object with a getter for relations based on meta.relationships
which was constructed by json-api-normalizer
, but only if you fetch data by /posts/1?include=comments
endpoint
Would you consider to extend/change or accept a PR to select related data also based on
meta['/posts/1/comments']
If so please answer and would be nice to talk about ideas.
This library seems only meant for reading. How do you suggest making a change to the object and saving back to the redux store?
Per the subject... if a "bundle.js" and a "bundle.min.js" are included in the dist, then the unminified version can be used during development which will permit better testing and debugging.
I am running in to a performance issue I am investigating. For now I got around this by explicitly including the source script.
https://www.smashingmagazine.com/2017/05/json-api-normalizer-redux/
Section: 3. Fetching The Data From The Store
Hello,
core-js is a bit huge and IMO not necessary anymore. Is it possible to remove it from dep?
After converting a JSON response to an Object, can I convert it back to plain JSON in order to send it to the server? If yes, how? Can you give me an example?
So... as a suggestion, please consider making the 'id' argument optional. If omitted (or 'null'), return an array of a results for the given data type.
Similarly, optionally accept an array of ID numbers as the 'id' argument. When an array, return all results for the given ID numbers as an array and throw a warning or error for any missing.
This and your normalizer are both a great, tidy package to augment API-driven state management, I look forward to utilizing them both. Thanks!
@yury-dymov I've found a couple of situations where it would have been useful to still have inside the object the type
information from the jsonapi. I propose including it as a special $type
attribute, which is guaranteed not to interfere with normal existing attributes, given that the jsonapi spec does not allow $ as a valid character in a member name.
With this change in place build
for this jsonapi object
{
id: '1',
type: 'items',
attributes: { name: 'Hello World' },
}
would become this:
{
id: '1',
name: 'Hello World',
$type: 'items',
}
Makes sense? I can make the change and submit a pull request.
Update: my bad, I see that there's already an option to do this. It fits me as it is, and I'll use it. But I'd argue that the use of the name type
is not ideal. An object could perfectly have an attribute named type
, and this option would override it. Granted, it's an edge case, but it could technically happen.
When building an object from a normalized structure, properties that originate from a relationship in the normalized structure are non-enumerable. When iterating over the properties of the built object or when using Object.assign on it, those properties are lost.
Is there a specific reason for not using enumerable: true
in line 85?
OH HELLO AGAIN.
So, I've run in to an issue when working with polymorphic relationships. When using redux-object to build an asset for production, the data type of a polymorphic relationship is lost. There are probably various hacky ways to resolve this... but one clean way is to include an attribute "type" on the build output objects. This would probably be default off but could be turned on with an option control, and it would first check to see if the native object has an attribute or relationship called "type" before attempting to overwrite it. This does means that 'type' becomes special and developers should avoid using 'type' in their model design... but, since we're talking about JSON API spec here, everyone should already be treating it as reserved if they are smart. But, that's why it should be optional.
Do you agree with this? If yes, I'll submit a PR. If no, I'll do it on my own branch. Thanks.
If I have something like the following:
const mapStateToProps = (state, ownProps) => ({ user: build(state.data, 'user', ownProps.userId) })
The resulting component is going to re-render at every possible opportunity, because build returns what is technically a new object on any state change.
I see that there's a cache
parameter (but assumed it was an internal used in the recursion), so perhaps this is a case of not documenting the last parameter, rather than new functionality.
With reference to this Issue / proposed PR on json-api-normalizer: yury-dymov/json-api-normalizer#25
I am looking to affect something similar for a PR to this package as well, however in this space it's left a bit more open to discussion with regards to how to address meta properties on relationships, or if it should be done at all. I just want to start a conversation, in the meantime I am going to experiment a bit and see if I can come up with something which works well.
Hello,
It could sound weird to you but I'm currently using this lib for my JSON:API with json-api-normalizer, which is really useful, but I don't use redux however.
That's why I would like to know if we could remove "resolved" via an option?
Thanks!
Thanks for the great library. I'm having error cannot process cyclical state
. I have a scenario in which a team has a coach in relationship property(based on jsonapi specification) and that coach has the same team in its relationship and the cycle continues. How do we build such a response?
Sorry if this is confusing, but this is real blocker for serializing state which redux does.
Does not work with IE11.
const post = build(state.data, 'post', '2620');
What if there is no post 2620
in state.data
?
According to implementation, build
will return { id: 2620 }
, but I think it's incorrect - I would expect null
in such case.
There is no tests for the case, as I can see.
If I have users
that have many books
, and I make a request for a user
object without sideloading the related books, then I might have the following structure in my store:
{
users: {
'1': {
id: '1',
type: 'users',
attributes: {},
relationships: {
books: {
data: {
[{
id: '1',
type: 'books'
}, {
id: '2',
type: 'books'
}]
}
}
}
}
},
books: {}
}
If I run const user = build(state.entities, 'users', '1')
then I get the user back. However if I then run user.books
I get [null, null]
. I would expect that I should get
[{
id: '1',
type: 'books'
}, {
id: '2',
type: 'books'
}]
I have experienced many use cases where I don't need the full related object, just the id (to provide a link to that object, for instance) and so I won't include
the related resources.
I'd be happy to make the changes if you would accept a PR!
I don't think the Lodash methods used in the package are really required. They have ES6 equivalents. Removing Lodash as a production dependency would be nice.
It is used very deliberately in the unit testing, so it should still be retained as a development dependency.
If you agree, I will implement, test and submit a PR after you publish the previous PR, but I am just checking that you agree first before committing time to it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.