-
Notifications
You must be signed in to change notification settings - Fork 3
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
Prepare for react async rendering #10
Comments
That's really bad. |
At the first glance, it looks like it makes the custom state management impossible. |
|
Nah, it's even worse. In case of the WeakMap keys are weak and there's no iteration support. Thus, there seems to be no obvious way to use it as an actual weak reference storage. Therefore, (1) is unsolvable unless there will be the way to delay the event subscription for the record/collection tree to some moment of time after the aggregation tree is created (special { delayEventsSubscription } constructor option is needed, + the method to subscribe for changes recursively). Could be done in the Events level (the listener's operation mode when listenTo doesn't actually create the back reference at the source, + the method to make sure events are enabled. To solve (3), all updates to the state made in the lifecycle hooks must be muted. Well, it can be done. |
I think componentWillReceiveProps still can be used if its only is run after component is mounted and if it only updates listeners that already exists it should be fine. But there are more problems with the current form of MVx, for example the lastest version of react-hot-loader is not working. I'm looking into replacing it with a much simpler solution that only implements listenToProps and completely skip the state replacement. I have noticed that its problematic for fresh programmers to use setState in some components and state.xx = in others. Also we are only using listenToProps so the lib is overkill. I did some measurments for the initializing of a component with pure react (0.1ms) and MVx (5.0ms). With my solution below the result was about 0.2ms. So this is a first prototype that does what I want.:
(edited so listeners map is not shared between instances) |
Well. That can be solved rather easily. Look at that commit. But we at VDMS/Volicon don't use This setState is still better than standard. For instance, this statement will deeply merge the state's a member, instead of just overwriting it. this.setState({ a : { b : 1 }}); The biggest problems of standard state is that you can't easily pass its part down as "mutable prop" (well, you can with the help of our NestedLink lib), and that you can't easily reuse parts of the complex state (even with NestedLink). |
If you want local component updates on external state changes, it can be done like that. static state = {
items : someGloballyAvailableRecordOrCollectionInstance
} Passing props is not really required. |
That's quite sad, but I never saw it in the profiler's top in our application. First of all, the Component has no lifecycle hooks defined by itself, so there should be zero overhead. When you start using features like state or props, it will dynamically construct the mixin with the required features only. For the case of the props change listener, it's this: const ChangeHandlersMixin = {
componentDidMount(){
handlePropsChanges( this, {}, this.props );
},
componentDidUpdate( prev ){
handlePropsChanges( this, prev, this.props );
},
componentWillUnmount(){
handlePropsChanges( this, this.props, {} );
}
};
function handlePropsChanges( component : any, prev : object, next : object ){
const { _changeHandlers } = component;
for( let name in _changeHandlers ){
if( prev[ name ] !== next[ name ] ){
for( let handler of _changeHandlers[ name ] ){
handler( next[ name ], prev[ name ], component );
}
}
}
} And the handler itself looks like this: function( next, prev, component : any ){
prev && component.stopListening( prev );
next && component.listenTo( next, changeEvents || next._changeEventName, component.asyncUpdate );
} That's all you'll get in your component when using just props listeners, and as you can see, there is no extra code there. To optimize it, however, you should avoid stopListening/listenTo calls in favor of on/off, and avoid for-in loops. The reasons it's not optimized yet is that React-MVx is designed with different architectural patterns in mind.
Because of that reasons, there is practically no any noticeable overhead, and I have to note that our app has really complex UI operating with large data sets of thousands of items. |
So, React-MVx is written to support that architectural approach I briefly described above. If you prefer to go with mobx style (singletons + local events subscription), you don't really need ReactMVx Component. The simple decorator will do (as you described), and there is really nothing magical about the way how it's done. It will save you few KBs. |
But I don't like mobx style. It's memory hungry, too magical thus it's easy to make a mistake, and there are no real-world performance problems which can justify that as an approach of the first choice even for the complex UI case. I'd say all the problems really start with a "single source of truth" and "state management is hard" mantras and singletons. They want it to be hard, so they made it to be hard. |
Hi again, I'm sorry that this issue ended up as an architectural discussion, it was not my intent! I'm not trying to say that MVx is bad or anything either, we rely heavily on it and type-r! I dont think our architectural approach is so much different from yours, this is basically what we do: For example a chat room we have some kind of root element that holds the collection of chat messages, responsible for fetching more and so on. It holds the collection in its state so its updated on changes. Then it renders children and passing down chat message models (records) as props. The chat message component has a lot of different children that renders different parts but as we pass along the model the leafs can care for and rerender only on changes that are relevant. No where are we updating the actual model from within one of these leaf but all actions are sent directly to the server. So with MVx we dont have to spread out all props and pass them along, that is convenient and also only the relevant components are rerendered and not all parents in between the "root" and the leaf; great for performance. What I'm saying is not there are some things in MVx that we dont use and others we do use that can be worked around (putting something in a state for changes can be handled manually with on/off etc). And since MVx is conflicting with some of the inner parts of React it might be wise to look over the implementation without changing any Reacts internals, like the state handling for example. I dont really know why its not working with the new react-hot-loader but it complains and fails on some react.prototype manipulation. Recreating the great stuff that MVx offers might be possible to do without the "enhancing" parts! And back to the original topic! It will probably break some code for some but moving all init code that depends on being cleaned up by componentWillUnmount to componentDidMount seems possible. componentWillReceiveProps can still be used but make sure it only add listeners and such after component is really mounted (looks like it should be fine as it is now?) |
Please. It's wonderful to have architecture discussions. I don't have an idea why it became sorta tabu in the community. Btw, there are troubles with stores API as well. Need to be redesigned as they are changing the context API. #12
In this case, you have to take some additional actions to prevent the double render, because the whole component tree will be rendered anyway on the root state changes. If you use You might have other reasons to use local change subscription, though. Not sure I understood it well.
Well, the single thing we do is overriding the What I can do though is to move it to the |
Yep, even now the majority of event subscription is being handled in
It the worst case, if it will seriously break something inside of the React we will have to move the record state to the different member like I have to admit that the React team actually works really hard to make custom state management solutions impossible. They may even succeed once, but not this time. :) |
And speaking of the standard React state - it has no any single advantage over the Type-R record. It shouldn't be even faster to access and manipulate with. The record uses the real class for its attributes, which is being optimized by JIT as a real structure, not the hash. |
React 16.3 introduce some new lifecycle hooks and they will deprecate some of the old one in the next release. Also they are very clear with that the constructor, componentWillMount and some others must be side effect free.
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
Currently in MVx uses the constructor and componentWillMount for setting up stuff, like state and starts listen to props. That will lead to leaks later on when react decides to cancel a job and never mount the component at all (componentWillUnmount will never be called). React suggests that all code that add listeners that is supposed to be cleaned out in componentWillUnmount should be added in componentDidMount.
The text was updated successfully, but these errors were encountered: