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

react hot load doesnt work #13

Closed
vans163 opened this issue May 21, 2019 · 22 comments · Fixed by #14
Closed

react hot load doesnt work #13

vans163 opened this issue May 21, 2019 · 22 comments · Fixed by #14

Comments

@vans163
Copy link

vans163 commented May 21, 2019

Using react hotloader

import {hot} from 'react-hot-loader'
export default hot(module)(App)

Does not work, is there a way to get it to work without having the state reset every time a hotoad happens?

@dai-shi
Copy link
Owner

dai-shi commented May 21, 2019

gaearon/react-hot-loader#1088
I hope they are working on it.

@vans163
Copy link
Author

vans163 commented May 21, 2019

Thats unrelated AFAIK, that's for compile errors (that got fixed) + local component hooks which act as local component state, which does not work regardless.

Global hooks via react-hooks-global-state work more like Redux here, and need a shim for the Hot Loader I would assume. (So state gets carried over after hotload, perhaps using some as simple as writing to window. instead of to a global var)

import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import {hot} from 'react-hot-loader'

import { createGlobalState } from 'react-hooks-global-state';

const initialState = {
  counter: 0,
  text: 'hello',
};
const { GlobalStateProvider, useGlobalState } = createGlobalState(initialState);

const Counter = () => {
  const [value, update] = useGlobalState('counter');
  return (
    <div>
      <span>Count:{value}</span>
      <button type="button" onClick={() => update(value + 1)}>+4</button>
      <button type="button" onClick={() => update(value - 1)}>-1</button>
    </div>
  );
};

const TextBox = () => {
  const [value, update] = useGlobalState('text');
  return (
    <div>
      <span>Text:{value}</span>
      <input value={value} onChange={event => update(event.target.value)} />
    </div>
  );
};

const App = () => (
  <StrictMode>
    <GlobalStateProvider>
      <h1>Counter</h1>
      <Counter />
      <Counter />
      <h1>TextBox</h1>
      <TextBox />
      <TextBox />
    </GlobalStateProvider>
  </StrictMode>
);

ReactDOM.render(<App />, document.getElementById('app'));
export default hot(module)(App)

I think here

const initialState = {
  counter: 0,
  text: 'hello',
};
const { GlobalStateProvider, useGlobalState } = createGlobalState(initialState);

on a hotload this gets called again. This should only be called once per init of the full page? Any help to test this theory would be appreciated.

Here is how hotload works for Redux

import { createStore } from 'redux'

import rootReducer from './reducers'

const configureStore = () => {
  const store = createStore(rootReducer)

  if (process.env.NODE_ENV !== "production") {
    if (module.hot) {
      module.hot.accept('./reducers', () => {
        store.replaceReducer(rootReducer)
      })
    }
  }

  return store
}

export default configureStore

Need I similar shim here I think, but Iv no idea how to implement it.

@dai-shi
Copy link
Owner

dai-shi commented May 21, 2019

Oh, I see your point. Sorry about that.

local component hooks which act as local component state, which does not work regardless.

So, local state doesn't work on hot reload?

@vans163
Copy link
Author

vans163 commented May 21, 2019

Ahh so I am a little new to ES6, but I put the state into its own module (same like example 11 deep does it, example 1 I was using is incorect), and it seems to work as long as I do not hotload the state.js file itself. Which is good enough I guess.

@vans163 vans163 closed this as completed May 21, 2019
@vans163 vans163 reopened this May 21, 2019
@vans163
Copy link
Author

vans163 commented May 21, 2019

Actually this works "good enough" but if you have more complex state.js, such as with reducers or import reducers, itl wipe the state clean each hotload, so maybe a better fix needed, leaving it open.

@dai-shi
Copy link
Owner

dai-shi commented May 21, 2019

So, I reproduced the issue locally. What I experienced is that it doesn't reset the count, but the button no longer works. I need to dig into it. 🤔

@dai-shi
Copy link
Owner

dai-shi commented May 21, 2019

Interesting, I'm only guessing what react-hot-loader is doing, but it keeps hook state (as in useState). This library stores globalState outside of the hook state, and needs to be sync.

@dai-shi
Copy link
Owner

dai-shi commented May 21, 2019

@vans163 Can you try if #14 fixes your case?
It's not published in npm yet, you can install from github.

npm install 'dai-shi/react-hooks-global-state#trick-hot-loader'

@dai-shi
Copy link
Owner

dai-shi commented May 21, 2019

Here's the code I tried:

import React from 'react';
import { hot } from 'react-hot-loader/root';

import { createGlobalState } from 'react-hooks-global-state';

const initialState = {
  counter: 0,
  text: 'hello',
};
const { GlobalStateProvider, useGlobalState } = createGlobalState(initialState);

const Counter = () => {
  const [value, update] = useGlobalState('counter');
  return (
    <div>
      <span>Count:{value}</span>
      <button type="button" onClick={() => update(value + 1)}>+1</button>
      <button type="button" onClick={() => update(c => c + 1)}>+1</button>
    </div>
  );
};

const App = () => {
  const [count, setCount] = React.useState(0);
  return (
    <GlobalStateProvider>
      <div>
        <h1>Hot Load Example</h1>
        <h3>Local</h3>
        <span>count:{count}</span>
        <button onClick={() => setCount(count + 1)}>+1</button>
        <button onClick={() => setCount(c => c + 1)}>+1</button>
        <h3>Global</h3>
        <Counter />
      </div>
    </GlobalStateProvider>
  );
};

export default hot(App);

@vans163
Copy link
Author

vans163 commented May 22, 2019

seems I had same problem. state gets wiped? Not sure maybe I did not purge old packages propely but my package.json says now "react-hooks-global-state": "github:dai-shi/react-hooks-global-state#trick-hot-loader",

@vans163
Copy link
Author

vans163 commented May 22, 2019

This patch made things even worse actually. Now when I keep state in a seperate module, it wipes the state clean if I hotload any part of the app. Before this patch, it would wipe the state clean only when I would hotload state.js

@dai-shi
Copy link
Owner

dai-shi commented May 22, 2019

Hmmm, can you share reproduction code?

@vans163
Copy link
Author

vans163 commented May 22, 2019

import { createGlobalState } from 'react-hooks-global-state';

const { GlobalStateProvider, setGlobalState, useGlobalState } = createGlobalState({
  counter: 0,
  text: 'hello',
});

export { GlobalStateProvider, setGlobalState, useGlobalState };
import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import {hot} from 'react-hot-loader'

import { GlobalStateProvider, useGlobalState } from './state';

const Counter = () => {
  const [value, update] = useGlobalState('counter');
  return (
    <div>
      <span>Count:{value}</span>
      <button type="button" onClick={() => update(value + 1)}>+2</button>
      <button type="button" onClick={() => update(value - 1)}>-1</button>
    </div>
  );
};

const TextBox = () => {
  const [value, update] = useGlobalState('text');
  return (
    <div>
      <span>Text:{value}</span>
      <input value={value} onChange={event => update(event.target.value)} />
    </div>
  );
};

const App = () => (
  <StrictMode>
    <GlobalStateProvider>
      <h1>Counter</h1>
      <Counter />
      <Counter />
      <h1>TextBox</h1>
      <TextBox />
      <TextBox />
    </GlobalStateProvider>
  </StrictMode>
);

ReactDOM.render(<App />, document.getElementById('app'));
export default hot(module)(App)

With master, if you change this page, edit for example +2 to +3, state is presevered. With your latest branch, if you edit +2 to +3, state gets wiped.

@dai-shi
Copy link
Owner

dai-shi commented May 22, 2019

I needed to delete this line,

ReactDOM.render(<App />, document.getElementById('app'));

but if I remove it, it just works. Can you confirm the environment? Are you using the patched react-dom?

@dai-shi
Copy link
Owner

dai-shi commented May 22, 2019

Here's roughly how I did:

  1. npx create-react-app example-app
  2. cd example-app
  3. npm run eject
  4. npm install --save-dev react-hot-loader
  5. edit config/webpack.config.js
  6. npm install dai-shi/react-hooks-global-state#trick-hot-loader
  7. yarn add react-dom@npm:@hot-loader/react-dom

@vans163
Copy link
Author

vans163 commented May 22, 2019

Right I see, but if you dont rerender does hotloader still update the react components?

@dai-shi
Copy link
Owner

dai-shi commented May 22, 2019

Do you mean if you edit a file, the component is update in browser? Yes.

@dai-shi
Copy link
Owner

dai-shi commented May 22, 2019

@vans163 I made a full reproduction. Please give it a try.
hot-load-example.zip


Not quite sure, but this small example seems working without the patched react-dom.
I might misunderstand something.

@vans163
Copy link
Author

vans163 commented May 23, 2019

Your example works. I see, you seperate index.js and App.js.

@dai-shi
Copy link
Owner

dai-shi commented May 23, 2019

seperate index.js and App.js

Does this fix your example too?

If you are ok with this, I'll publish a new version.

@vans163
Copy link
Author

vans163 commented May 24, 2019

This fixes it if I do exactly as your example. And put render inoto index.js not App.js, which is the correct way to do it anyways. yes can merge

@dai-shi
Copy link
Owner

dai-shi commented May 24, 2019

Published: https://www.npmjs.com/package/react-hooks-global-state/v/0.10.0

@vans163 Thanks for your contribution!

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

Successfully merging a pull request may close this issue.

2 participants