-
Notifications
You must be signed in to change notification settings - Fork 47.2k
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
Bug: MobX-like observer pattern doesn't work with Fast Refresh because Hooks don't get detected #20417
Comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I'd love to find a solution (or at least workaround) for this issue. |
Potential work around for mobx is to use
will become like this:
|
Sadly, it doesn't work in my case. I've tried arrow functions, Observer component and HOC: neither of them do the trick, the checkbox stays checked after refresh either way. |
@rizkyramadhan I have tried the |
Yeah, it's pretty wierd... I ended up ditching snowpack altogether, Now I use customized esbuild for my bundler. |
Hi, can you talk more about the customized esbuild solution? |
@rizkyramadhan I dont use snowpack, nor do I know what it is. We have a "normal" setup with react-refresh-webpack-plugin without any sort of abstraction like CRA. React Hot Loader V3 was the last version that worked properly for our mobx / react apps and we'd stayed on it if it were not for incompatibilities coming with new React versions. RHL v4 never properly worked with similar behaviour than react-refresh. Disappointing that we had a better developer experience 5 years ago. |
Basically I build my own bundler, first I tested simple esbuild solution using react, and modify the esbuild parameters with various esbuild plugins according to my needs, and setup chokidar to watch my src folders for changes. When source changes, I broadcast the changes using esm-hmr protocol, currently it's still using naive dom replacement without react-refresh. But it's really fast, even comparable to react-refresh due to esbuild. I use fastify to serve the files. The most important thing is mobx and another npm package that I use works out of the box. |
When no signatures on both sides, consider as not equal signature, forcing remount and not refresh
Have you looked into why? |
I think you want to look at these tests: react/packages/react-refresh/src/__tests__/ReactFreshBabelPlugin-test.js Lines 209 to 222 in 7b84dbd
Add something like the example in your repro. What does it get transformed to? Why doesn't it get the same treatment like The relevant logic is somewhere around here, probably: react/packages/react-refresh/src/ReactFreshBabelPlugin.js Lines 632 to 640 in 7b84dbd
|
There seems to be a misunderstanding in this thread. First, the original issue reported in #20417 (comment) no longer reproduces on CodeSandbox. I think that this was a bug in how CodeSandbox implemented Fast Refresh, which they fixed in codesandbox/codesandbox-client#5442. So it was fixed in February. This is confirmed by #20417 (comment):
Then, the comment in #20417 (comment) has nothing to do with that problem. The linked thread is about some checkbox not working, which is not at all the symptom described in this thread. So this was incorrectly lumped together with this issue. That issue is likely just something being broken in MobX. Then, #20417 (comment) is a new comment about some problem but it doesn't specify what the problem actually is, or offer any reproducing case. So I can't help here and I can't guess which of the problems above it refers to. In conclusion:
Thanks! |
After trying more, I can repro this now. So my conclusion seems incorrect. I'll take a closer look at this... |
Yayyy I can repro this in CRA. This is a good sign the problem is on our side. |
I have this problem with a plain webpack setup. No CRA or any abstraction. If you need a repo let me know. |
Hmm. So the problem here seems to be is that the |
This is a pretty exotic and unidiomatic pattern tbh. But it makes sense that MobX does it since it needs to know what has been read. |
I get the motivation for why you'd want to structure it this way, but there is some tension here between the way React works, and the way MobX tries to make it work. Unfortunately, the issue is right at the middle of that tension. React does not work reactively based on mutation. Like you noted, we have different paths. There's a number of things MobX can do to try to hide it away but we need to be clear that it'll never be fully in line with the paradigm. Here's a concrete example that has nothing to do with Fast Refresh. Say you start here: export default function SignIn() {
// read some mobx stuff
} Then you refactor it like this: export default Wrapper(props) {
return <SignIn {...props} />
}
function SignIn() {
// read some mobx stuff
} From React's point of view, this is a perfectly normal refactor. People do these literally every day and nothing breaks. (If we're being pedantic, refs kind of break, but But I think the This illustrates that it's a leaky abstraction, and there's only so much you can do to hide it away. So I would encourage to always wrap things in If it's a problem for tests, you can mock MobX out. But something has to give here. |
Thanks for the feedback, I'll see if I can come up with a solution that can get the best of both worlds. To be honest I've been checking interesting projects like mobx-jsx but it's more an experiment than anything. It was already hard to get companies to use Mobx a couple of years ago, now asking them to not use React would be mission impossible ; ) |
@AoDev |
I guess I have a misunderstanding of how observer works. Isn't
Because that's what the utility does. Note that this utility is mostly used for big app sections, like a root feature if you will, the other components are just plain observers. |
Correct it's not the same, since 1) RHL works on a per file basis and 2) references to MyComponent might refer to the unwrapped component (for example in the |
Thank you both for your time. I've got my mobx-react recipes since |
I was always curious... what does “M” stand for? Michel? :-) |
It was a bad word play: a mob of observables; MOBservable 😅. Since it is
so fine grained in its observation, there would be gazillion observables
more than when using e.g rxjs.
…On Fri, 26 Mar 2021, 19:15 Dan Abramov, ***@***.***> wrote:
I was always curious... what does “M” stand for? Michel? :-)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#20417 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAN4NBHE7HE6RWMRXGEOYMTTFTMNBANCNFSM4UUBJ4DA>
.
|
@gaearon Eventually I find out where the problem happen. It's because Also. I need to update react-refresh version in dependencies of plugin-react-refresh. So can you give me the version tag of react-refresh with this fix? Thanks a million :P |
Sorry I'm late for the party, we are in a holiday:) |
It don’t see how it would break render prop usage. I’d need a more specific example to explain why it doesn’t. |
|
Solves the problem for us, when used together with FredKSchott/snowpack#3015. Thanks so much for fixing, Dan! |
If anyone using |
@Jack-Works I am currently dealing with this issue. I updated all of the involved libraries to their current versions as of today:
I've been following all these discussions and still run into react complaining about mismatched hook invocations after a hot update that touches hooks in a component. |
@janv hi I'd like to fix this problem but please provide an example reproduce repository because I'm not familiar with Mobx. |
I prepared a minimal example here: https://github.com/janv/mobx-refresh-example |
I added |
Hm, are you sure? https://drive.google.com/file/d/1T1ObxHqJ8N5j0mKoAhZUaH8kEooCz853/view |
Ok so the problem is HMR works, but when hooks counts differ, it doesn't recover well. Let me see why this happens |
I actually tried adding a JS component too, using the |
Hmm so can you @gaearon look at this problem? |
Hi. I think i solved this problem inside react-refresh. I disabled the next update if the component name is "observerComponent". I don't know if I should do a PR as this is a mobx specific issue and maybe the react team doesn't want to include: Maybe it's possible to add a property in the component function to disable refresh in any component that needs it: Component.disableReactRefresh = true, but I'm not sure that's a good thing |
React version: 17.0.0
Steps To Reproduce
Link to code example:
https://codesandbox.io/s/react-refresh-webpack-plugin-rendered-more-hooks-than-during-the-previous-render-issue-ezcrz?file=/src/Comp.js
The current behavior
You get "Rendered more hooks than during the previous render" error
The expected behavior
Should hot reload and re-mount the component.
The source of the issue have two parts:
react-refresh
consider the components as compatible, which is not always true, as in the reproreact/packages/react-refresh/src/ReactFreshRuntime.js
Lines 126 to 132 in 9aca239
I've filed an issue also for the webpack plugin: pmmmwh/react-refresh-webpack-plugin#266
Mobx related issue: mobxjs/mobx#2668
The text was updated successfully, but these errors were encountered: