-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider mobx #2153
Comments
The performance structure is over my head, but may I suggest thinking with Redux instead of Mobx? Redux is a much smaller package (though it's more likely to require external libs for support) and it's also a lot more popular component in React apps, which means no additional weight what so ever for some users. |
This will be a breaking change for me though. We use immutable.js and referential equality heavily and Slate was a perfect fit for my use case. I think is there a way to expose an API that capture custom immutable.js collection that user can use to effect rerendering? I can see the power of mobx but if the value was not managed by mobx, isn’t it the same issue as well? Although mobx handle computed value magically. Is there a way to mix as well? |
Redux helps manage complex state. State management is simple in Slate. We are passing one If we change the value of one Slate It would be a breaking change; however, all of the custom stuff you are doing wouldn't be required any more. All you'd have to do is put whatever custom state you were rendering from into a mobx value (basically an Object) and use that. mobx handles the wiring for you so you could get rid of all your referential equality code. That's the main benefit. I've run into the problem myself where I add a value that didn't have a custom reference equality check and Slate doesn't render the update. With mobx, that won't happen. It's not the same issue as managing the state somewhere else for two reasons:
In 2, imagine adding a single letter to the DOM due to a Contrast that to the mobx Slate node being updated. The node knows it needs to update one specific Component. There is no node-traversal. We end up with three benefits:
So it's a big change. Yucky. Messy. It will have long-term benefits but non-trivial pain in the short-term... |
I suggest a simple improvement on the performance in the short term. A large document typically has a long list of top level nodes. In
If the children is a long list with 10K elements, there will be 10K To improve the performance, we can past the change object to the |
@thesunny I understand your point of view. The auto-subscription and dependency graph of mobx really help with common programming errors. Technically, I will have to re-evaluate if Slate change to mobx, I don't depend on Slate data model since many versions ago so it won't be a painful switch but I depend on Redux a lot and its something much more set to stone than Immutable.js as I uses redux-saga a lot. my understanding is that mobx and redux is 2 very different system. One focus on automatic managed subscription for mutation while the other focus on snapshot based event driven system (which basically oversubscribe at every events). My concern is that is there a escape hatch that allow us to propagate the final changes from slate (in mobx) to redux and bridge between the 2 world? Edit: Actually, we'll likely expose But I not sure how that will affect controlled component and how we repopulate it with initiate set of data first. Also, how do remote changes coming outside of mobx system get updated. Do we have to track 2 different state tree, one redux one mobx (with mobx-state-tree)? |
I didn't think about that. My gut says to use a regular prop passed into Slate and do |
Sorry I missed your post. I'm trying to think of how that might work. You could have Things that may trip it up are if two edit: Not sure how you'd get the new props into the |
Another option is to use immer rather than immutablejs. Immer is from the same author as Mobx. It would preserve ability to have immutability but with a more modern library that has a friendlier API. |
I've read through this a few times now and I have to admit I'm having trouble grokking what tangible benefit MobX will provide for both performance and preventing bugs. @thesunny Did you have a specific example in mind where performance would be improved? You also mentioned some bugs that would have been prevented with MobX, do you have specific examples in mind? One other consideration: what effect would this change have on schema validation and normalization? We still need to do tree traversal there. |
@newtack @ericdem breaking your questions down: Performance:Let's say you have 2 identical arrays of 1000 JavaScript objects which we'll call arrayA and arrayB. The objects look something like Now let's say we update the object at index One way to find out how to update arrayB is to loop through all 1000 values and compare the objects to each other and see if they are equivalent. If they are not, then we do a set Another way to keep the two arrays in sync is to keep a reference in each object in It's more nuanced in that MobX can actually target the change down to the individual text node so if you typed one letter, instead of comparing the entirety of Slate document against the entirety of the old version of the Slate document, you are only updating the single span that the text you typed is in. shouldComponentUpdate bugs:In order to improve performance, React looks for a method in a React Component called Slate does this to improve performance. But it only compares the values Slate expects will result in a change to the render. The bugs appear because sometimes you need to re-render based on a value Slate doesn't expect will change the render. This can happen, for example, if say you want to change the This is a simple example, but there are many reasons to change the way Slate renders its document based on outside state. Schema Validation and Normalization:MobX won't affect the way validation and normalization work. |
Performance graphically illustrated: Below is the way Slate works now. We changed one of the values from
With MobX we make one comparison
|
I would like to add one potential benefit of So looking at it, immutable-js vs immer, immer is no-brainer for me, immer is the clear winner:
Regarding Mobx vs Immer, I dont wanna giving any arguments about performance and bugs, as I am not Mobx expert (I used it only in 1 project), but by using Mobx data structures you are again open to immutable disadvantages (conversions and learning new API). But I cannot make a fair comparison here. I am Redux fan not Mobx, as imho Mobx introduces too much magic and it has really serious debugging issues (in comparison to Redux) |
@thesunny Thanks, thats a great explanation! On performance: One thing I do wonder about is how often we are going to run into a flat data structure like this in slate. It doesn't seem likely that you would have 1000 blocks at the top level. I'd expect comparison performance to be closer to a O(log(N)) due to the tree structure nature of the document. I also wonder a bit about the performance hit of having to continually attach / detatch the two way binding in MobX. I've seen performance problems around two way binding when working with Angular 1 with a large amount of variables in the app. The performance impact of pure React seems a bit easier to understand. @klis87 It might be good to open a separate discussion issue for replacing Immer with Immutable.js. It seems like a separate discussion which might be promising on its own! 😄 |
I think the current code is O(n). As a document gets bigger, we add more top level blocks (e.g. paragraphs) of similar size rather than having bigger paragraphs. I believe MobX would make it O(1) although the first render would still be O(n). With respect to binding being a performance issue, and from what I've read, mobx in real life scenarios outperforms custom I think the O(1) nature is what I like about MobX because, generally, if Slate performs well in a small document, it should perform similarly in a big one. I'd like to ask about "it has really serious debugging issues (in comparison to redux)". Can you be specific? |
@thesunny I remember that sometimes nothing was shown in console in case of an error, or it wasnt as easy to find based on stacktrace what part of code was problematic (I used Mobx both as separate classes and inside React class components). Also, I had some issues when I used |
Personally I would vote for MobX too, but (if possible) the ideal solution is that Slate should be agnostic. With a modular approach one should be able to use Mobx, Immer, or whatever new thing that comes in the future. |
Hey folks, I'm going to close this in favor of #2345 now. I don't think we'd end up using MobX, as React seems to be moving away from monolithic state management in general (which I'm in favor of). But the idea of being able to use plain JavaScript constructs is incredibly appealing for other reasons. I'd love if you could add your thoughts there. Thanks! |
Do you want to request a feature or report a bug?
infrastructure change
What's the current behavior?
Slate uses immutable internally to improve editor performance.
When a render happens, Slate compares nodes for reference equality and skip the re-render if they are the same.
There are two issues with this (1) as the document gets bigger there are more objects that need to be compared on each render so performance can become an issue in a big document even if the change is small (2) there are cases where data outside the Immutable document effects the render. These need to be handled on a property by property basis in
shouldComponentUpdate
and is a cause for easy to miss bugs in custom Slate code where the outside value changes but the editor isn't re-rendered.What's the proposed behavior?
So I'm posting this because I've been thinking about this for a while. I understand this would be a huge change and would be hard to do anytime soon if ever. Mostly, I just wanted to start a dialog even if it's short lived so I can move on from thinking about this.
Mobx basically works in reverse. When you update the data structure, mobx is already tied to the right components to do the renders. In other words, it doesn't need to traverse the entire document tree in order to update the right component. This should solve the performance problem as documents get larger.
Furthermore, as long as any other render dependency values are mobx values, we don't have to worry about updating the
shouldComponentUpdate
logic. This removes that problem case which seems to show up regularly in Issues.Practically speaking, two things of note:
The text was updated successfully, but these errors were encountered: