Skip to content
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

webpack survey #341

Closed
sokra opened this issue Jul 3, 2014 · 39 comments
Closed

webpack survey #341

sokra opened this issue Jul 3, 2014 · 39 comments

Comments

@sokra
Copy link
Member

sokra commented Jul 3, 2014

Hey guys.

I would like to make a small survey about webpack usage. I would appreciate it, if you find some time to fill out this little survey.

The idea is to get more info about the users and the use cases, so I can focus on the important tasks.

I'll post a summary of the results in this issue.

Thanks

PS: Feel free to share it with other webpack users you know. Not everyone is reading github issues and I don't want to add a message into the library.

@jhnns
Copy link
Member

jhnns commented Jul 4, 2014

done()

@gaearon
Copy link
Contributor

gaearon commented Jul 5, 2014

Did.

@gaearon
Copy link
Contributor

gaearon commented Jul 5, 2014

@sokra Thanks for taking time to answer on SO. I still spent an hour struggling to enable HMR because it is only briefly mentioned on this page that you need to enable HotModuleReplacementPlugin (and it is assumed a person already knows how to use plugins at this point). Could you please make this more prominent and also mention this in your SO answer? My biggest beef was that “small testcase” on that page did not say anything about enabling HotModuleReplacementPlugin so I assumed it isn't needed with dev server and passing hot: true was enough. Stupid, I know, but the docs must mention all the steps.

@gaearon
Copy link
Contributor

gaearon commented Jul 5, 2014

Oh my god. Now that you explained it, I tried HMR and it blew me away. Hot module replacement is the most exciting thing I've seen in frontend dev since React. You really need to explain and document the hell out of this feature. It's the freaking future of frontend dev.

@sokra
Copy link
Member Author

sokra commented Jul 8, 2014

@gaearon Yes it's pretty cool.

@gaearon @rpflorence I merged your videos about Code Splitting and Hot Module Replacement with React and now there is this loader which decorates a React component with Code Splitting and Hot Module Replacement. You need to change only one line... 😄

cc @petehunt

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

💣

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

I haven't had time to check yet so.. Will this reconcile DOM, or will it force re-render of the component? Keeping the DOM proved real hard for me. (Although it was night and I didn't think very hard.)

I really wanted the update to occur without doing unmount/mount for the changed component.

In my demo, I had to replace component's __proto__ to update its functions without killing it (crazy and works only for render), and add a global flag that prevents shouldUpdateReactComponent from bailing out because it thinks the “new” component is a different one (since it doesn't even try to reconcile in this case).

So this is roughly the code I had (I know but it was 5am):

var mounted = [];
if (module.hot) {
  module.hot.accept('./cover', function () {
    var OldCover = sectionWidgets['cover'];
    Cover = require('./cover');
    sectionWidgets['cover'] = Cover;
    window.IGNORE_SHIT = true;
    _.each(mounted, function (s) {
      _.each(s._renderedComponent._renderedChildren, function (child) {
        if (child.__proto__ === (new OldCover.componentConstructor()).__proto__) {
          child.__proto__ = (new Cover.componentConstructor()).__proto__;
        }
      });
      s.forceUpdate();
    });
    window.IGNORE_SHIT = false;
  });
function shouldUpdateReactComponent(prevComponentInstance, nextDescriptor) {
  // TODO: Remove warning after a release.
  if (prevComponentInstance && nextDescriptor &&
-      (prevComponentInstance.constructor === nextDescriptor.constructor) && (
+      (prevComponentInstance.constructor === nextDescriptor.constructor || window.IGNORE_SHIT) && (
        (prevComponentInstance.props && prevComponentInstance.props.key) ===
        (nextDescriptor.props && nextDescriptor.props.key)
      )) {

I was thinking that, in order to support hot-swapping without unmount/mount, we'd need some kind of support in React core, but maybe I was mistaken. I thought that maybe we'd need some kind of unmountComponentAtNode(false) that doesn't destroy the DOM.

cc @petehunt regarding last paragraph

@sokra
Copy link
Member Author

sokra commented Jul 8, 2014

I created a "proxy" React component which has the real component in it's state. In render it renders the real component with transferPropsTo. In the accept handler I call setState with the updated component. React manages the rest...

The Code Splitting part works similar by calling setState once the component is available...

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

What's the easiest way to launch the example? I tried webpack-dev-server example but seems like I'm doing it wrong

@sokra
Copy link
Member Author

sokra commented Jul 8, 2014

cd example
webpack-dev-server --hot
http://localhost:8080/webpack-dev-server/bundle

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

Says Cannot find module 'webpack'.

If I do npm install webpack, says

ERROR in ./app.jsx
Module not found: Error: Cannot resolve module react-proxy in /Users/dan/Documents/Projects/react-proxy-loader/example
 @ ./app.jsx 8:8-35

@sokra
Copy link
Member Author

sokra commented Jul 8, 2014

ups... I hold my repos in a node_modules folder...^^
I'll update it

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

@sokra

If I edit Component C, it unmounts (before re-mounting again). You can say this by

  componentWillUnmount: function () {
    window.alert('Ouch! Unmounting :-(');
  }

and trying to switch to "Component C (other param)".

Its state also is reset on updates:

  getInitialState: function () {
    return {
      counter: 0
    };
  },

  render: function() {
    return (
      <p onClick={this.incrementCounter}>
        This is component C ({this.props.param}).
        <br />
        My state is {this.state.counter}.
      </p>
    );
  },

  incrementCounter: function () {
    this.setState({
      counter: this.state.counter + 1
    });
  }

(If you click on it a couple times and then edit, it'll get back to 0.)

What would be really cool is if React allowed us to preserve state (and DOM) between updates. I imagine it could work like this:

  • Save .state
  • Unbind React and its handlers from nodes but keep their structure and react-id intact
  • Render “fresh” component at the root node, which should pick up react-ids, just like with server rendering—but somehow with state we saved

I think it requires some support in React core. This would enable true hot swapping.

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

See this:

The same logic is used for custom components. If they are not of the same type, React is not going to even try at matching what they render. It is just going to remove the first one from the DOM and insert the second one.

When we load the next version, React thinks it's a totally different component and bails out of reconciliation.

@sokra
Copy link
Member Author

sokra commented Jul 8, 2014

Actually I tried to move the state to the new component, but react gave me some warnings that I shouldn't access state and setState.

The reconciliation is really a problem. Is it possible to override this behavior? But if we would tell React that it's the same component it wouldn't update it:

We decided that the two custom components are the same. Since components are stateful, we cannot just use the new component and call it a day. React takes all the attributes from the new component and call component[Will/Did]ReceiveProps() on the previous one.

It doesn't seem to be possible without accessing react internals... I need to start reading react sources...

@gaearon
Copy link
Contributor

gaearon commented Jul 8, 2014

@sokra

Relevant file is shouldUpdateReactComponent.js. However simply returning true won't work. In this case, React will just reuse component instance (and thus, render and other methods will belong to the old code).

(I re-read your last comment and yeah, that's exactly what you said :-).

From my chat with @vjeux, it seems that changing unmountComponentAtNode to keep React nodes won't work either since reconciliation only happens for virtual DOM nodes.

We need to substitute React component instance on-the-fly without unmounting.

@sokra
Copy link
Member Author

sokra commented Jul 8, 2014

We need help from a react developer...

@vjeux
Copy link

vjeux commented Jul 8, 2014

cc @spicyj @sebmarkbage @petehunt who may be able to help

@valpackett
Copy link

So I've said "webpack doesn't read source maps" in the survey... But then I've discovered that it does, it's just in a separate plugin. So I guess this should be more clearly documented?

@petehunt
Copy link

petehunt commented Jul 8, 2014

The problem is that references to component descriptors could be stored in any number of places. What we could do is wrap all components in "proxy" components which look up the "real" component in some mapping. Then all we'd have to do is rehydrate the state of each component instance which is easy: keep a state table keyed by (_rootNodeID, _mountDepth) and update it on componentDidUpdate(). Basically would add a local version of https://github.com/petehunt/react-multiplayer/blob/master/index.js to every component by default.

Easiest way to implement this would be to monkeymatch React.createClass()

@sokra
Copy link
Member Author

sokra commented Jul 9, 2014

I tried this. I serialized all states in the tree before a component update and called setState for each of the new components. This does work. It'll keep the states, but it will still unmount and remount the components. It also render the components twice because componentDidUpdate happens after the component is already rendered. I would need something like "a (rendered) component in the sub-tree will mount".

https://github.com/webpack/react-proxy-loader/blob/tree-state/mixinReactProxy.js#L19-L24

Ok let us assume we would inject the state correctly (or don't have state at all). There is still the problem that the DOM nodes are completely removed. This makes sense accoring to the documentation, because the new component is not the same as the old component, but here they may have very similar sub-trees.

The algorithm will not try to match sub-trees of different components classes. If you see yourself alternating between two components classes with very similar output, you may want to make it the same class. In practice, we haven't found this to be an issue.

Now it's an issue. Can I opt-in for react doing this for a single component? I. e. something like shouldReconcileComponents(newComponent, oldRenderedComponent) that let react reconcile even different components (maybe also moving state to the new component). It should be called on the direct parent component. (I think it should be part of ReactCompositeComponent.)

@sokra
Copy link
Member Author

sokra commented Jul 12, 2014

Lets start with some simple charts from the survey.

project importance

How you using webpack?
How do you plan to use webpack in the near future?

image

Looks pretty promising...

project size (modules count)

How big is your codebase?

image

instagram.com has over 1000 modules. 😄 Maybe @petehunt reveals more stats in his talk at OSCON...

features

Which features are you using currently?
Which features are you not using currently but plan to use?

image

(top 10 used/planed features)

@gaearon
Copy link
Contributor

gaearon commented Jul 12, 2014

I tried this. I serialized all states in the tree before a component update and called setState for each of the new components. This does work. It'll keep the states, but it will still unmount and remount the components.

I think Pete suggested something different than what I've seen you try. Implementing a proxy class that forwards method calls to the freshest component version and monkey-patching createClass should probably solve the problem, since .constructors will match in shouldUpdateReactComponent.

This is probably beyond my current React/JS abilities but I'll try to play with this approach in my spare time.

Edit: on a second thought, it's quite probable that I misunderstood Pete. Not sure.

@petehunt
Copy link

No you're correct, I did a bad job in describing it. You need to keep some sort of mapping from a class to all of its instances and overwrite each instance with the correctly autobound methods.

@gaearon
Copy link
Contributor

gaearon commented Jul 12, 2014

Thanks for clarification. How would you go about overwriting autobound methods? I imagined it this way (but I'm not sure if this is possible or makes sense):

  • Constructor (accessible from ConvenienceConstructor.componentConstructor) created inside createClass is wrapped into a membrane
  • This membrane would register for module hot updates: we can do this from the loader
  • When update is available, the membrane somehow rewires Constructor and ConvenienceConstructor (??). It probably changes some prototypes, rewrites some methods (only ones defined in ReactCompositeComponentInterface?) on some of the prototypes (only ConvenienceConstructor?). Maybe it should update ConvenienceConstructor.componentConstructor, Constructor.ConvenienceConstructor, ConvenienceConstructor.originalSpec, or maybe not—I'm a bit lost because I don't know the internals very well.

Basically I want something like updateClass(spec).

@gaearon
Copy link
Contributor

gaearon commented Jul 12, 2014

I missed the part about the instances. But I'd need to keep track both of components and their descriptors? Or just the component instances?

@gaearon
Copy link
Contributor

gaearon commented Jul 12, 2014

I think I got it working! Will publish in an hour or so.

@gaearon
Copy link
Contributor

gaearon commented Jul 13, 2014

@sokra @petehunt

I made a loader that preserves state and doesn't unmount nested components, given that all of them are loaded with it. It's not perfect (currently it relies on displayName and adds a mixin to spec instead of returning a proxy), but it seems to work, at least for simple use cases.

@gaearon
Copy link
Contributor

gaearon commented Jul 13, 2014

@sokra Can you please take a look at master in react-hot-loader? It works if I specify loader in require for every component, but if I put { test: /\.jsx$/, loaders: ['react-hot', 'jsx'] } in config, it goes into infinite recursion. I'm not sure if I need to require things differently from inside the loader to avoid it..

Edit: nevermind, I sorted it out

@sokra
Copy link
Member Author

sokra commented Jul 13, 2014

@gaearon I see you already solved it. The !! prefix does the trick.

The replaceCreateClass version also looks less "hacky". That's good... ;)

@gaearon
Copy link
Contributor

gaearon commented Jul 13, 2014

@sokra Yeah right. Still, this needs to be solved. I'm trying a membrane approach (similarly to how React does internally with createDescriptorProxy) but it's going to take me a while.

@jhnns
Copy link
Member

jhnns commented Jul 14, 2014

Thanks for the stats @sokra!

@sokra
Copy link
Member Author

sokra commented Jul 20, 2014

The survey says that many people want to generate a stylesheet as separate file. So I focused on getting the #170 CSS bundle done. It should work now. I'll add a documentation page soon.

Another complain was that the examples miss some documentation. So as next step I'll add a short text to each example.

Than I'll create an example web app that features: react, separate stylesheet, server-side prerendering, hybrid routing with multiple entry points + History API, Code Splitting, long-term-caching and a commons chunk.

@sokra
Copy link
Member Author

sokra commented Jul 20, 2014

dev tools

Which features are you using for development?

image

SourceMaps are booming... I didn't expected that much.

interface

Which interface are you using?

image

@shama A lot demand for the gulp-webpack integration...

resource type

Which kind of resources do you process with webpack?

image

(top 10)

JS was excepted, but CSS is also pretty big. I try to improve the CSS support (see above post).

community

What do you think is needed for the webpack community?

image

As expected... I'm working on that... but I need help. Everyone can edit the documentation. Please use this ability 😄

@gaearon
Copy link
Contributor

gaearon commented Jul 24, 2014

I finally wrote a blog post covering Hot Module Replacement with React. Thanks again for helping out and explaining stuff.

@sokra
Copy link
Member Author

sokra commented Jul 25, 2014

Pretty awesome... I'll add it to the list of tutorials.

Maybe you could add two new lifecycle methods componentWillBeReplaced and componentWasReplaced. This way the user could do stuff like changing timeouts.

You could also add a note about excluding stuff in your config:

module: {
  loaders: [
    { test: /\.jsx$/, loader: 'react-hot', exclude: [
      /some-component\.jsx$/
    ] },
    { test: /\.jsx$/, loader: 'jsx' },
  ]
},

I would really like to see somebody using this in production...

@jhnns
Copy link
Member

jhnns commented Aug 4, 2014

@gaearon that's one of the hottest things I've seen in front-end development for some time 😀

react's diff mechanism and webpack's hmr is a perfect match!

@sokra
Copy link
Member Author

sokra commented Dec 6, 2014

Update 😄

project importance

How you using webpack?
How do you plan to use webpack in the near future?

image

project size (modules count)

How big is your codebase?

image

features

Which features are you using currently?
Which features are you not using currently but plan to use?

image

(top 10 used/planed features)

dev tools

Which features are you using for development?

image

interface

Which interface are you using?

image

resource type

Which kind of resources do you process with webpack?

image

(top 10)

community

What do you think is needed for the webpack community?

image

@ryanflorence
Copy link

I answered as "clearer examples" rather than "more examples". Last time I looked there was a lot of code to make authoring the examples easier which actually makes understanding them more difficult.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants