Skip to content
This repository has been archived by the owner on Feb 14, 2023. It is now read-only.

Subscribe to deep-nested global state properties. #4

Open
theKashey opened this issue Nov 1, 2018 · 10 comments
Open

Subscribe to deep-nested global state properties. #4

theKashey opened this issue Nov 1, 2018 · 10 comments
Labels
feature A new feature is requested. help wanted The owner cannot resolve this on their own.

Comments

@theKashey
Copy link

Object key usage tracking is working only one level down, thus would be a source of performance issues for a wide set of cases, normally manageably by Redux.

Take a look how it was made in react-easy-state. Unfortunately (to you), that a-year-old-library is much more usable than this brand-new

@quisido
Copy link
Collaborator

quisido commented Nov 1, 2018

Hi @theKashey,

The Object key listener is deliberately shallow. Can you elaborate on the performance issues that you believe this to cause? I am aware that it will cause re-renders on components that access global.x.y when global.x.z updates. I am open to alternative implementations, but most seem to come with greater drawbacks than shallow listeners. Are there other concerns?

react-easy-state uses Proxies, which cannot be polyfilled and are not supported by 13% of users. They are a great solution and are absolutely the future of global state management. When browsers inevitably adopt better support for them, I am excited at the opportunity to use them in my projects, even this one. I intend to instate Proxies when support surpasses 95%, but I may do it before then with a fallback to the current system.

I do want to be clear that ReactN deliberately does not aim to be as full-featured as Redux. There are trade offs of meaningful features in exchange for a shallow learning curve, less boilerplate, and more maintainable code. It is up to each project to determine what is more important. There is no one global state management solution that applies to every project. 👍

I would not personally consider react-easy-state more usable, but I am glad to receive community feedback on how an intuitive implementation would look! If you have suggestions for what aspects of their implementation you think would improve ReactN's user experience, please share them.

Thanks for taking the time to look at the repository. I look forward for more feedback.

@quisido
Copy link
Collaborator

quisido commented Nov 1, 2018

I have added the behavior to the documentation here. Let me know if this closes your issue.

@theKashey
Copy link
Author

You may just keep tracking nested keys. At lest for objects, as long for arrays without proxy that could be not so easy.
Tracking is not a big problem by itself, it's much harder to manage result. Like

<div>{global.x}</div> - I should update on `x` change
<div>{global.x.y}</div> - I should update on `x.y` change, but could ignore `x` chage
<div>{global.x.y} + {global.x.z}</div> - I should update on `x.y` and `x.y` change
....
<div>{someFunction(global.x)}</div> - oh. I don't know.

To be honest - I dont know how to manage key tracking on the "render" level.
Anyway - some thought could be found here - https://github.com/respond-framework/remixx/blob/master/docs/initial-plan.md

@quisido
Copy link
Collaborator

quisido commented Nov 2, 2018

It is not possible to know whether I accessed global.x as the entire object or global.x.y as a property. For JS to get to property y on object global.x, it must first access global.x.

const a = this.global;
const b = a.x;
const c = b.y;
return c; // I only want to subscribe to this.global.x.y changes.

Boilerplate can solve this issue, but I am strictly opposed. At that point, one should just be using Redux.

It is possible to listen to only the deepest accessed properties. If I accessed global.x.y, I can ignore subscriptions to global.x.

This would work in all cases except where both the object and a property on that object are used. I cannot think of a use case for this, outside of JSON.stringify(global.x) being used in the same component as global.x.y. I don't want to not account for that, but it's such an outlier case. I'll sleep on it and leave it open to more discussion, as well as more alternative algorithms.

@quisido
Copy link
Collaborator

quisido commented Nov 2, 2018

Optional boilerplate may be acceptable for the sole purpose of performance benefits, instead of necessity.

Something like this.globalIgnore('x') in the componentDidMount for components that access global.x.y.

It doesn't add boilerplate to the typical user case, but I'm not sure how intuitive it is.

I'm also not completely sold on adding listeners to nested objects, What if that object is an instance, such as new Promise? Returning a modified clone sounds like it can cause bugs. I do like the idea in concept, though.

I've also just opened this withGlobal HOC issue for discussion on it as a viable choice for optional boilerplate.

@quisido quisido changed the title Document objectGetListener behavior Can we subscribe to deeply nested global state? Nov 2, 2018
@quisido quisido added feature A new feature is requested. help wanted The owner cannot resolve this on their own. labels Nov 2, 2018
@quisido quisido mentioned this issue Nov 2, 2018
@quisido
Copy link
Collaborator

quisido commented Nov 6, 2018

Hey @theKashey ,

I don't mean to spam you with these discussions. I've just released deep-proxy-polyfill in an attempt to get close as possible to Proxy behavior. I may change the syntax a bit, but that would be a rough draft of how deep-nested subscriptions would work.

It's passing tests that it is subscribing correctly. Some performance needs to be taken into consideration (having getters on every parent object firing), but ultimately I think it's a good starting point.

With a community go-ahead, I'll implement it in ReactN.

@theKashey
Copy link
Author

Looking good! If you will also use Proxy when possible - it will be just perfect!

@quisido quisido added the support A user is requesting support. label Nov 21, 2018
@quisido quisido self-assigned this Nov 21, 2018
@quisido quisido changed the title Can we subscribe to deeply nested global state? Subscribe to Deeply Nested Properties Feb 15, 2019
@quisido quisido changed the title Subscribe to Deeply Nested Properties Subscribe to deep-nested global state properties. Apr 28, 2019
@nickfla1
Copy link

This would be so useful if implemented int ReactN!

@quisido quisido removed the support A user is requesting support. label May 15, 2019
@quisido quisido removed their assignment May 15, 2019
@yezyilomo
Copy link

yezyilomo commented Jun 22, 2019

How about using useUpdateState hook which does everything like useGlobal but it returns updateState instead of setState with API like.

[state, updateState] = useUpdateState("globalState")

updateState({action: "action-to-perfom", field: "field-to-update", value: "value-to-update"})

supported operations being assign, push, pop, remove, filter and others if there will be any,
and for nested fields/keys the field parameter is specified as x.y.z.

@yezyilomo
Copy link

yezyilomo commented Jun 22, 2019

useUpdateState in action.

setGlobal({
    user: {isLoggedIn: true, info: {name: "peter", emails: ["peter@gmail.com"]}, }
})

function User(props){
    [user, updateUser] = useUpdateState("user");
    
    let editName = () => {
        updateUser({action: "assign", field: "info.name", value: "jason"})
        // Or simply updateUser({field: "info.name", value: "jason"})
    }
    
    let addEmail = () => {
        updateUser({action: "push", field: "info.email", value: "peter@me.com"})
    }
    
    let removLastEmail = () => {
        updateUser({action: "pop", field: "info.email"})
    }
    
    let removeEmail = () => {
        updateUser({action: "remove", field: "info.email", value: "peter@me.com"})
    }
    
    let filterEmails = () => {
        updateUser({action: "filter", field: "info.email", value: (email) => email != "peter@me.com"})
    }
    
    let logOutUser = () => {
        updateUser({action: "assign", field: "isLoggedIn", value: false})
        // Or simply updateUser({field: "isLoggedIn", value: false})
    }
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature A new feature is requested. help wanted The owner cannot resolve this on their own.
Projects
None yet
Development

No branches or pull requests

4 participants