-
Notifications
You must be signed in to change notification settings - Fork 36
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
Inject hot reload variables? #34
Comments
👍 and this would not be special to redux. It is just one use case. |
@guybedford, do you perhaps have any tips? It would be very nice if this works in such a way it's compatible with how module loaders will natively work in the browser some day (With systemjs, but without the es6 module loader). |
Am I right in thinking the argument here is about injecting variables when the module is executed and not as a later function call? I'm wondering how such a syntax could be mimicked in ES modules, perhaps something like - import previousState from '@hotstate'; Or something similar might work instead of the So it would be possible, but it's not an elegant use of the module loader by any means, and I also don't like the idea that the previous state "sticks around" with its bindings from a memory perspective (as module imports are permanent bindings in the module scope. Say for example another function export that uses eval could then indirectly cause the state to never be garbage collected. The nice thing about the I know it doesn't look quite as aesthetically pleasing, but I'm really not sure the benefit is worth the cost to do something better here in a way that works well with modules. |
I'm trying to use this with https://github.com/gaearon/redux-react and it complains when the state changes. It would be really nice to have a way of injecting variables before the module execution. Because now, I don't see many options how to solve that. On load, the script has no idea whether it was hot loaded or not. And some need to behave differently if they were. This Provider component has to take state from the previous module, otherwise it won't work. It could be done in the It would be enough, if the hook would be executed, before the module itself. edit: I'd be ok with using the babel transforms to do that. Like https://github.com/tyscorp/react-transform-jspm-hmr/ |
@mikz I also came across this problem and still have to look into this. Are you sure this would fix the issue? Isn't it that the provider react component persists (over hot reloads) (even though we recreate the code that created him) and changing it's store isn't supported? It looks like if we can retain the same store instance and just rehydrate it this wouldn't be a problem. Then again that would be easier is we had this injecting option, and maybe that is what you meant? Then again if the Provider component would have a method that would allow us to change the store, we could call that from the __reload and that issue would also be solved. |
@peteruithoven React remembers somewhere in virtual dom, that Provider was already there. So when the component that renders Provider is reloaded, it tries to update existing Provider with new store. The key here, is that the I done that by storing it in To follow the DevTools suggestion, we should retain the store, and call Other way, not requiring global variable would work, if modules would be aware of hot reloading. export let store = configureStore(reducers)
function render() {
ReactDOM.render((
<Provider store={store}>
<div>
<HelloWorld />
<Counter />
<DevTools />
</div>
</Provider>
), document.getElementById('app'))
}
export function __reload(deletedModule) {
store = deletedModule.store
store.replaceReducer(reducers)
render()
}
if (!__hot_) { render() } Basically, deferring the execution, until Edit: In the end I store the variable in Window when module is about to be unloaded. https://github.com/mikz/jspm-react-hot-reload/blob/6204b2d407afb9857d665ebe625143dc2d36b507/src/index.js#L11-L20 |
@mikz you could just wrap your renderer in a function: import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import configureStore from './store'
import DevTools from './containers/dev_tools'
import Counter from './containers/counter'
import HelloWorld from './containers/hello_world'
import reducers from './reducers/index';
export let store;
export function __reload(deletedModule) {
store.replaceReducer(reducers)
}
export function render() {
store = store || configureStore(reducers)
ReactDOM.render((
<Provider store={store}>
<div>
<HelloWorld />
<Counter />
<DevTools />
</div>
</Provider>
), document.getElementById('app'))
} And then have a import {render} from 'renderer';
render(); |
@guybedford unfortunately, systemjs-hot-loader calls Tried to do that as mikz/jspm-react-hot-reload@3fc30e2 but no dice. |
Ah, right. We should ensure that |
@guybedford That would work IMO. It is nasty, that you have to export those variables, but guess the other way would be to have some API available, that would export them to some anonymous module and the newly loaded one would get that as a parameter. But still, better this than nothing :) |
Relevant issue: reduxjs/react-redux#223 |
I'd prefer thinking along the lines of Guy's first idea. |
I do think the function wrapping approach in #34 (comment) is preferable, by supporting the |
But, in most cases having the __reload in modules deeper isn't useful because there might be multiple instances of that module. I'm probably missing something but I don't understand why you would prefer that #34 (comment) approach I've tried out my idea: Simplified usage example: import {getStore as getPrevStore} from 'capaj/systemjs-hot-reloader/prevInstance.js';
let store;
if(getPrevStore) {
// use existing store
store = getPrevStore();
store.replaceReducer(reducer);
} else {
// create new store
store = createStore(reducer);
}
export function getStore() {
return store;
} Curious what you guys think. |
Don't know. Somehow it feels dirtier than saving it to const store = window.__store || createStore(reducer);
if(store === window.__store)
store.replaceReducer(reducer)
export function __unload() {
window.__store = store
} I understand that might cause some issues when the module is loaded several times in several contexts. So it would need some unique id persisted through the reloads. But maybe it is just because it is in wrong module and it should be in one that can't be used several times.
What I like about the I guess the difference here is that webpack does not reimport the whole tree on single change? And every component can opt in into the reload process? I'm not saying it is right or desired with SystemJS. Maybe, we should think about what is ideal for SystemJS ecosystem and for general development guidelines. Here is my list:
I think the second is driven by hot reloading replacing all the tree. I'm probably ok with that as I have not found any drawbacks. But I can imagine code where it could cause some pain, like double initializing some rendering etc. I understand it should be turned off in Cheers! |
@mikz, I have to agree, especially on point 1. I totally see the downside of that in my approach. What if we use the fact that, an imported module, that isn't changed isn't reloaded and can retain state? We could create a module just for storing state over hot reloads. This would allow storing state, without introducing new api and without storing state in window. import getReloadStore from 'hot-reload-store';
const hotStore = getReloadStore('myproject:index');
let store;
if(hotStore.store) {
// use existing store
store = hotStore.store;
store.replaceReducer(reducer);
} else {
// create new store
store = createStore(reducer);
hotStore.store = store;
} |
@peteruithoven will update my post with another point. The code should be easily shipped to production. |
I've made a simple example demonstrating my last idea; |
For a more crude way to force reload React components, like the Provider or the Router: |
@peteruithoven by using #51 does this mean that the hot reloader will reload when we make changes to our redux reducers? Having problems hot reloading when making changes to the redux reducers at the moment. |
I would only use #51 (unmountComponentAtNode) when there is no other option, for example when using the React-router. (and even then there is probably a better solution out there). import { createStore } from 'redux';
import getHotReloadStore from './utils/getHotReloadStore.js';
const hotStore = getHotReloadStore('myproject:store');
export default function configureStore(reducer) {
let initialState;
if (hotStore.store) initialState = hotStore.store.getState();
const store = createStore(reducer, initialState);
hotStore.store = store;
return store;
} |
Tracking here -> alexisvincent/systemjs-hmr#2 |
Webpack works with
module.hot
variables in modules. See: https://webpack.github.io/docs/hot-module-replacement.htmlHaving such variables would make some things seriously easier.
For Redux for example, the code in the following gist: https://gist.github.com/peteruithoven/59f188bfab035ce96948
Could turn into something like:
Would it be possible to "inject" such variables into hot reloaded modules?
The text was updated successfully, but these errors were encountered: