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

hot module reload #125

Closed
steida opened this issue Nov 27, 2016 · 20 comments
Closed

hot module reload #125

steida opened this issue Nov 27, 2016 · 20 comments
Labels

Comments

@steida
Copy link
Contributor

steida commented Nov 27, 2016

This works but it's not probably a good approach.
Why do we have to reset textContent on the server-side rendering? It looks like an implementation detail. Also, removeAttribute is probably wrong, because subscribing. Should I create empty node manually?

Note Root is render on module change in development.

const getFelaMountNode = () => {
  const node = document.getElementById('stylesheet');
  node.removeAttribute('data-fela-stylesheet');
  node.textContent = '';
  return node;
};

// We needs such Root for vanilla hot reloading.
const Root = ({ store }: Props) => (
  <Redux store={store}>
    <Fela mountNode={getFelaMountNode()} renderer={createFelaRenderer()}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Fela>
  </Redux>
);
@steida
Copy link
Contributor Author

steida commented Nov 27, 2016

Should I send PR?

@steida
Copy link
Contributor Author

steida commented Nov 27, 2016

Current implementation

const getFelaMountNode = () => {
  const node = document.getElementById('stylesheet');
  const parent = node.parentNode;
  if (!node || !parent) {
    throw new Error('missing stylesheet node for Fela');
  }
  const nextNode = document.createElement('style');
  nextNode.id = 'stylesheet';
  parent.replaceChild(nextNode, node);
  return nextNode;
};

@robinweser
Copy link
Owner

Where exactly would you add that? I never used hot reloading in any of my projects as I am using another approach to do stateful live devopment. I'd be really happy to officially support hot reloading with Fela!

@steida
Copy link
Contributor Author

steida commented Nov 27, 2016

In the Root.js https://github.com/este/este/blob/master/src/browser/app/Root.js

Este uses native (without plugins as recommended by Dan Abramov when app doesn't use local state) HMR. https://github.com/este/este/blob/master/src/browser/main.js#L31

@steida
Copy link
Contributor Author

steida commented Nov 27, 2016

For Fela it means, no private state in modules! When file is changed, the whole app is restarted, which is ok and stable, if app state is in Redux.

@robinweser
Copy link
Owner

Perhaps the best way for me to understand it, is to just show me all changes. It sounds kind of what I am using for a current project, but it's even more "dumb" I guess. I am syncing my whole Redux store to localStorage every single time it changes and intially load the store from localStorage as well. For remote development (e.g. mobile) or other browsers I use a simple server that syncs the store globally. It's quite helpful to have persistent development, but it needs to reload the page every time. Also any state needs to be part of Redux^^

@steida
Copy link
Contributor Author

steida commented Nov 27, 2016

localStorage is ok, stateless modules as well, but stateful js modules are tricky, must be handled like that https://github.com/este/este/blob/885358e7a78c59df3a7a7a3b218456194b63111b/src/common/configureMiddleware.js#L37

@steida
Copy link
Contributor Author

steida commented Nov 28, 2016

I will send you a PR.

@TheUltDev
Copy link
Contributor

Hot reloading worked fine for me. I'm not sure what would need to change in the project, seems to be implementation specific.

// Initial stylesheet w/ global styles
const renderer = createRenderer();
const createGlobalStyles = rules =>
  Object.keys(rules).forEach(selector =>
  renderer.renderStatic(rules[selector], selector));

createGlobalStyles(globalStyles);

// Initial render.
ReactDOM.render(
  <Fela renderer={renderer} mountNode={mountNode}>
    <Root store={store} />
  </Fela>
, appElement);

// Hot reload render.
// gist.github.com/gaearon/06bd9e2223556cb0d841#file-naive-js
if (module.hot && typeof module.hot.accept === 'function') {
  module.hot.accept('./app/themes', () => {
    renderer.clear();
    createGlobalStyles(require('./app/themes').globalStyles);
  });

  module.hot.accept('./app/Root', () => {
    const NextRoot = require('./app/Root').default;

    ReactDOM.render(
      <NextRoot store={store} />
    , appElement);
  });
}

@steida
Copy link
Contributor Author

steida commented Nov 29, 2016

It works for me as well. Still I would rather:

/* @flow */
import App from './App';
import React from 'react';
import configureFela from '../../common/configureFela';
import { BrowserRouter } from 'react-router';
import { Provider as Fela } from 'react-fela';
import { Provider as Redux } from 'react-redux';

type Props = {
  store: Object,
};

// This should be part of Fela.
// TODO: https://github.com/rofrischmann/fela/issues/125
const getFelaMountNode = () => {
  const node = document.getElementById('stylesheet');
  const parent = node.parentNode;
  if (!node || !parent) {
    throw new Error('missing stylesheet node for Fela');
  }
  const nextNode = document.createElement('style');
  nextNode.id = 'stylesheet';
  parent.replaceChild(nextNode, node);
  return nextNode;
};

// We needs such Root for vanilla hot reloading.
const Root = ({ store }: Props) => (
  <Redux store={store}>
    <Fela mountNode={getFelaMountNode()} renderer={configureFela()}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Fela>
  </Redux>
);

export default Root;

But it has a low priority.

@johanneslumpe
Copy link
Contributor

johanneslumpe commented Nov 30, 2016

I can't even get it to work properly, but that might be because I'm using code splitting...

UPDATE
turns out i had to give the provider component a unique key whenever the components got updated, so that after clearing the stylesheet the provider would get re-mounted and would then re-render the styles.

@steida I wonder why tt worked for you with without this...

@robinweser
Copy link
Owner

robinweser commented Dec 12, 2016

Anything we can do at Fela, to improve the hot reloading experience?

@steida
Copy link
Contributor Author

steida commented Jan 2, 2017

@rofrischmann I believe this belongs to Fela https://github.com/este/este/blob/c7e1138e51be6a8c27ba534dc8ecd0c74a695a57/src/browser/app/Root.js#L13

As for hot reloading experience, we don't need anything else. Este uses the most frictionless hot reloading, just rerendering everything. It works because whole state is in the Redux anyway.

That's why I have to call getFelaMountNode(), because app was rerendered because hot reload. What do you think?

@srigi
Copy link

srigi commented Jan 13, 2017

I don't think this should be part of Fela. I'm creating an app that renders into <iframe>. So stylesheet need to be passed down to the child document of the page.

If fela will handle styleshhet node like this, it will be disaster for this kind of applications.

@robinweser
Copy link
Owner

I also don't see this being part of Fela at all, but we should add instructions to the documentation on how to actually enable hot module replacement with Fela. Any thoughts are welcome.

@queckezz
Copy link

queckezz commented Aug 25, 2017

For anyone still looking for how to do hot-reloading with webpack, this is how I did it:

Root.js

import getRenderer from '../lib/css-renderer'
import { render } from 'fela-dom'

if (module.hot) {
  module.hot.dispose(() => {
    const renderer = getRenderer()
    renderer.clear()
    render(renderer)
  })
}

lib/css-renderer.js

import { createRenderer } from 'fela'

const renderer = createRenderer()

const getRenderer = () => {
  applyGlobalStyles(renderer)
  return renderer
}

export default getRenderer

@RafalFilipek
Copy link
Contributor

RafalFilipek commented Sep 13, 2017

@queckezz can you add applyGlobalStyles function? And how you connect renderer to Provider.

Thank you.

@SpencerCDixon
Copy link

@rofrischmann I don't see any docs on how to get fela to work with hot module reloading. Does anyone have any guidance on how to get it to work with storybook??

@brianespinosa
Copy link
Contributor

Hi @rofrischmann I was digging through the docs to see if I could find an example for hot module reloading as referenced in this comment but I was not able to find anything except this closed issue. Was this example removed in a previous version perhaps?

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

No branches or pull requests

10 participants