Recoil
Recoil copied to clipboard
Atoms don't update when using react-hot-loader
inline atom with component will cause strange behavior, not reload the 1st time, but refresh the full page on the 2nd change.
What's an inline atom?
@aappddeevv defined in the same file of its component.
const state = atom({key: 'key', default: 1});
export function Comp() {
...
}
@naivefun - Using atoms defined in the same module as a component that uses it is a very common pattern. Perhaps there is something else unique about your environment?
I am also seeing this behavior. Again, this is only a problem with hot reloading. Currently I'm storing my atoms on the window object (using Symbol.for
as the key) to work around this.
This is breaking with hot reload
/**
* Dependencies
*/
import React, { useCallback, useState } from "react";
import styled from "styled-components/macro";
import pickBy from "ramda/src/pickBy";
import pathEq from "ramda/src/pathEq";
import toPairs from "ramda/src/toPairs";
import append from "ramda/src/append";
import identity from "ramda/src/identity";
import memoizeWith from "ramda/src/memoizeWith";
import { atom, useRecoilState } from "recoil";
import ComponentListBox from "./component-listbox";
/**
* States
*/
const nodeWithPath = memoizeWith(identity, (path) =>
atom({
key: path,
default: [],
})
);
/**
*
*/
const StyledTree = styled.ul``;
const StyledNode = styled.li``;
const StyledEmptyNode = styled.li``;
/**
* node: [{meta, properties}]
*/
const Node = ({ path }) => {
const [node, setNode] = useRecoilState(nodeWithPath(path));
const [addComponent, setAddComponent] = useState(null);
const handleAppendComponent = useCallback(
(meta) => {
setNode((oldNode) =>
append(
{
meta,
nodes: toPairs(
pickBy(pathEq(["type", "name"], "node"), meta.props)
),
properties: {},
},
oldNode
)
);
setAddComponent({ displayName: "" });
},
[setNode]
);
const handleChangeComponent = useCallback(
(meta, index) => {
setNode((oldNode) =>
append(
{
meta,
nodes: toPairs(
pickBy(pathEq(["type", "name"], "node"), meta.props)
),
properties: {},
},
oldNode
)
);
setAddComponent({ displayName: "" });
},
[setNode]
);
return (
<>
{node.map((component, iComponent) => (
<StyledNode key={iComponent}>
<ComponentListBox
value={component.meta}
onChange={(meta) => handleChangeComponent(meta, iComponent)}
/>
{component.nodes.length > 0 && (
<ul>
{component.nodes.map((propNode, iPropNode) => (
<li key={iPropNode}>
<Node path={`${path}.${iComponent}.${propNode[0]}`} />
</li>
))}
</ul>
)}
</StyledNode>
))}
<StyledEmptyNode>
<ComponentListBox
onChange={handleAppendComponent}
label="Add a new component"
value={addComponent}
/>
</StyledEmptyNode>
</>
);
};
/**
* Component
*/
const Tree = ({ onSelectComponent }) => {
return (
<StyledTree>
<Node path="." />
</StyledTree>
);
};
export default Tree;
with this error when hot reloading
log.js:24 [HMR] Waiting for update signal from WDS...
react_devtools_backend.js:6 ./src/components/builder-page/component-props.js
Line 4:17: 'useState' is defined but never used no-unused-vars
Line 5:8: 'styled' is defined but never used no-unused-vars
r @ react_devtools_backend.js:6
printWarnings @ webpackHotDevClient.js:138
handleWarnings @ webpackHotDevClient.js:143
push../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:210
react_devtools_backend.js:6 ./src/components/builder-page/builder-page.js
Line 5:8: 'styled' is defined but never used no-unused-vars
r @ react_devtools_backend.js:6
printWarnings @ webpackHotDevClient.js:138
handleWarnings @ webpackHotDevClient.js:143
push../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:210
react_devtools_backend.js:6 ./src/components/builder-page/component-listbox.js
Line 6:17: 'useState' is defined but never used no-unused-vars
Line 7:8: 'styled' is defined but never used no-unused-vars
r @ react_devtools_backend.js:6
printWarnings @ webpackHotDevClient.js:138
handleWarnings @ webpackHotDevClient.js:143
push../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:210
react_devtools_backend.js:6 ./src/components/builder-page/component-props.js
Line 4:17: 'useState' is defined but never used no-unused-vars
Line 5:8: 'styled' is defined but never used no-unused-vars
r @ react_devtools_backend.js:6
printWarnings @ webpackHotDevClient.js:138
handleWarnings @ webpackHotDevClient.js:143
push../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:210
react_devtools_backend.js:6 ./src/components/builder-page/builder-page.js
Line 5:8: 'styled' is defined but never used no-unused-vars
r @ react_devtools_backend.js:6
printWarnings @ webpackHotDevClient.js:138
handleWarnings @ webpackHotDevClient.js:143
push../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:210
react_devtools_backend.js:6 ./src/components/builder-page/component-listbox.js
Line 6:17: 'useState' is defined but never used no-unused-vars
Line 7:8: 'styled' is defined but never used no-unused-vars
r @ react_devtools_backend.js:6
printWarnings @ webpackHotDevClient.js:138
handleWarnings @ webpackHotDevClient.js:143
push../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:210
recoil.development.js:118 Uncaught TypeError: Cannot read property 'error' of undefined
at recoverableViolation (recoil.development.js:118)
at registerNode (recoil.development.js:191)
at baseAtom (recoil.development.js:2236)
at atom (recoil.development.js:2294)
at component-tree.js:18
at memoizeWith.js:50
at _arity.js:11
at Node (component-tree.js:37)
at renderWithHooks (react-dom.development.js:14826)
at updateFunctionComponent (react-dom.development.js:17059)
at beginWork (react-dom.development.js:18644)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackDev (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
at beginWork$1 (react-dom.development.js:23235)
at performUnitOfWork (react-dom.development.js:22186)
at workLoopSync (react-dom.development.js:22162)
at performSyncWorkOnRoot (react-dom.development.js:21788)
at react-dom.development.js:11112
at unstable_runWithPriority (scheduler.development.js:653)
at runWithPriority$1 (react-dom.development.js:11062)
at flushSyncCallbackQueueImpl (react-dom.development.js:11107)
at flushSyncCallbackQueue (react-dom.development.js:11095)
at Object.batchedUpdates$1 [as unstable_batchedUpdates] (react-dom.development.js:21894)
at react-hot-loader.development.js:3045
at react-hot-loader.development.js:3004
recoverableViolation @ recoil.development.js:118
registerNode @ recoil.development.js:191
baseAtom @ recoil.development.js:2236
atom @ recoil.development.js:2294
(anonymous) @ component-tree.js:18
(anonymous) @ memoizeWith.js:50
(anonymous) @ _arity.js:11
Node @ component-tree.js:37
renderWithHooks @ react-dom.development.js:14826
updateFunctionComponent @ react-dom.development.js:17059
beginWork @ react-dom.development.js:18644
callCallback @ react-dom.development.js:189
invokeGuardedCallbackDev @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
beginWork$1 @ react-dom.development.js:23235
performUnitOfWork @ react-dom.development.js:22186
workLoopSync @ react-dom.development.js:22162
performSyncWorkOnRoot @ react-dom.development.js:21788
(anonymous) @ react-dom.development.js:11112
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
flushSyncCallbackQueueImpl @ react-dom.development.js:11107
flushSyncCallbackQueue @ react-dom.development.js:11095
batchedUpdates$1 @ react-dom.development.js:21894
(anonymous) @ react-hot-loader.development.js:3045
(anonymous) @ react-hot-loader.development.js:3004
Promise.then (async)
add @ react-hot-loader.development.js:3003
deepUpdate @ react-hot-loader.development.js:3061
Promise.then (async)
updateInstances @ react-hot-loader.development.js:3099
(anonymous) @ react-hot-loader.development.js:3113
hotSetStatus @ bootstrap:267
hotApply @ bootstrap:651
(anonymous) @ bootstrap:362
Promise.then (async)
hotUpdateDownloaded @ bootstrap:361
hotAddUpdateChunk @ bootstrap:337
webpackHotUpdateCallback @ bootstrap:57
(anonymous) @ main.9886f3792025f1ef799c.hot-update.js:1
index.js:1 The above error occurred in the <Node> component:
in Node (at component-tree.js:114)
in ul (created by component-tree__StyledTree)
in component-tree__StyledTree (at component-tree.js:113)
in Tree (at builder-page.js:19)
in BuilderPage (at site.js:29)
in Route (at site.js:28)
in Switch (at site.js:27)
in main (created by site__Main)
in site__Main (at site.js:26)
in Site (at root.js:37)
in Route (at root.js:36)
in Router (created by BrowserRouter)
in BrowserRouter (at root.js:35)
in IntlProvider (at root.js:30)
in ThemeProvider (at root.js:28)
in Base (at root.js:52)
in RecoilRoot (at root.js:51)
in ReduxDevToolsExtension (at redux.js:100)
in ReduxProvider (at root.js:50)
in Root (created by HotExportedRoot)
in AppContainer (created by HotExportedRoot)
in HotExportedRoot (at src/index.js:21)
in StrictMode (at src/index.js:20)
React will try to recreate this component tree from scratch using the error boundary you provided, AppContainer.
console.<computed> @ index.js:1
r @ react_devtools_backend.js:6
logCapturedError @ react-dom.development.js:19561
logError @ react-dom.development.js:19598
callback @ react-dom.development.js:20778
callCallback @ react-dom.development.js:12513
commitUpdateQueue @ react-dom.development.js:12534
commitLifeCycles @ react-dom.development.js:19892
commitLayoutEffects @ react-dom.development.js:22835
callCallback @ react-dom.development.js:189
invokeGuardedCallbackDev @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
commitRootImpl @ react-dom.development.js:22573
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
commitRoot @ react-dom.development.js:22413
finishSyncRender @ react-dom.development.js:21839
performSyncWorkOnRoot @ react-dom.development.js:21825
(anonymous) @ react-dom.development.js:11112
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
flushSyncCallbackQueueImpl @ react-dom.development.js:11107
flushSyncCallbackQueue @ react-dom.development.js:11095
batchedUpdates$1 @ react-dom.development.js:21894
(anonymous) @ react-hot-loader.development.js:3045
(anonymous) @ react-hot-loader.development.js:3004
Promise.then (async)
add @ react-hot-loader.development.js:3003
deepUpdate @ react-hot-loader.development.js:3061
Promise.then (async)
updateInstances @ react-hot-loader.development.js:3099
(anonymous) @ react-hot-loader.development.js:3113
hotSetStatus @ bootstrap:267
hotApply @ bootstrap:651
(anonymous) @ bootstrap:362
Promise.then (async)
hotUpdateDownloaded @ bootstrap:361
hotAddUpdateChunk @ bootstrap:337
webpackHotUpdateCallback @ bootstrap:57
(anonymous) @ main.9886f3792025f1ef799c.hot-update.js:1
index.js:1 TypeError: Cannot read property 'error' of undefined
at recoverableViolation (recoil.development.js:118)
at registerNode (recoil.development.js:191)
at baseAtom (recoil.development.js:2236)
at atom (recoil.development.js:2294)
at component-tree.js:18
at memoizeWith.js:50
at _arity.js:11
at Node (component-tree.js:37)
at renderWithHooks (react-dom.development.js:14826)
at updateFunctionComponent (react-dom.development.js:17059)
at beginWork (react-dom.development.js:18644)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackDev (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
at beginWork$1 (react-dom.development.js:23235)
at performUnitOfWork (react-dom.development.js:22186)
at workLoopSync (react-dom.development.js:22162)
at performSyncWorkOnRoot (react-dom.development.js:21788)
at react-dom.development.js:11112
at unstable_runWithPriority (scheduler.development.js:653)
at runWithPriority$1 (react-dom.development.js:11062)
at flushSyncCallbackQueueImpl (react-dom.development.js:11107)
at flushSyncCallbackQueue (react-dom.development.js:11095)
at Object.batchedUpdates$1 [as unstable_batchedUpdates] (react-dom.development.js:21894)
at react-hot-loader.development.js:3045
at react-hot-loader.development.js:3004 "
at AppContainer (eval at ES6ProxyComponentFactory (http://localhost:3000/static/js/0.chunk.js:60253:10), <anonymous>:14:7)
at HotExportedRoot (eval at ES6ProxyComponentFactory (http://localhost:3000/static/js/0.chunk.js:60253:10), <anonymous>:14:7)"
console.<computed> @ index.js:1
r @ react_devtools_backend.js:6
error @ react-hot-loader.development.js:294
componentDidCatch @ react-hot-loader.development.js:2399
callback @ react-dom.development.js:20783
callCallback @ react-dom.development.js:12513
commitUpdateQueue @ react-dom.development.js:12534
commitLifeCycles @ react-dom.development.js:19892
commitLayoutEffects @ react-dom.development.js:22835
callCallback @ react-dom.development.js:189
invokeGuardedCallbackDev @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
commitRootImpl @ react-dom.development.js:22573
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
commitRoot @ react-dom.development.js:22413
finishSyncRender @ react-dom.development.js:21839
performSyncWorkOnRoot @ react-dom.development.js:21825
(anonymous) @ react-dom.development.js:11112
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
flushSyncCallbackQueueImpl @ react-dom.development.js:11107
flushSyncCallbackQueue @ react-dom.development.js:11095
batchedUpdates$1 @ react-dom.development.js:21894
(anonymous) @ react-hot-loader.development.js:3045
(anonymous) @ react-hot-loader.development.js:3004
Promise.then (async)
add @ react-hot-loader.development.js:3003
deepUpdate @ react-hot-loader.development.js:3061
Promise.then (async)
updateInstances @ react-hot-loader.development.js:3099
(anonymous) @ react-hot-loader.development.js:3113
hotSetStatus @ bootstrap:267
hotApply @ bootstrap:651
(anonymous) @ bootstrap:362
Promise.then (async)
hotUpdateDownloaded @ bootstrap:361
hotAddUpdateChunk @ bootstrap:337
webpackHotUpdateCallback @ bootstrap:57
(anonymous) @ main.9886f3792025f1ef799c.hot-update.js:1
index.js:1 TypeError: Cannot read property 'error' of undefined
at recoverableViolation (recoil.development.js:118)
at registerNode (recoil.development.js:191)
at baseAtom (recoil.development.js:2236)
at atom (recoil.development.js:2294)
at component-tree.js:18
at memoizeWith.js:50
at _arity.js:11
at Node (component-tree.js:37)
at renderWithHooks (react-dom.development.js:14826)
at updateFunctionComponent (react-dom.development.js:17059)
at beginWork (react-dom.development.js:18644)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackDev (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
at beginWork$1 (react-dom.development.js:23235)
at performUnitOfWork (react-dom.development.js:22186)
at workLoopSync (react-dom.development.js:22162)
at performSyncWorkOnRoot (react-dom.development.js:21788)
at react-dom.development.js:11112
at unstable_runWithPriority (scheduler.development.js:653)
at runWithPriority$1 (react-dom.development.js:11062)
at flushSyncCallbackQueueImpl (react-dom.development.js:11107)
at flushSyncCallbackQueue (react-dom.development.js:11095)
at Object.batchedUpdates$1 [as unstable_batchedUpdates] (react-dom.development.js:21894)
at react-hot-loader.development.js:3045
at react-hot-loader.development.js:3004 "
at AppContainer (eval at ES6ProxyComponentFactory (http://localhost:3000/static/js/0.chunk.js:60253:10), <anonymous>:14:7)
at HotExportedRoot (eval at ES6ProxyComponentFactory (http://localhost:3000/static/js/0.chunk.js:60253:10), <anonymous>:14:7)"
console.<computed> @ index.js:1
r @ react_devtools_backend.js:6
logException @ react-hot-loader.development.js:1604
componentDidCatch @ react-hot-loader.development.js:2412
callback @ react-dom.development.js:20783
callCallback @ react-dom.development.js:12513
commitUpdateQueue @ react-dom.development.js:12534
commitLifeCycles @ react-dom.development.js:19892
commitLayoutEffects @ react-dom.development.js:22835
callCallback @ react-dom.development.js:189
invokeGuardedCallbackDev @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
commitRootImpl @ react-dom.development.js:22573
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
commitRoot @ react-dom.development.js:22413
finishSyncRender @ react-dom.development.js:21839
performSyncWorkOnRoot @ react-dom.development.js:21825
(anonymous) @ react-dom.development.js:11112
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
flushSyncCallbackQueueImpl @ react-dom.development.js:11107
flushSyncCallbackQueue @ react-dom.development.js:11095
batchedUpdates$1 @ react-dom.development.js:21894
(anonymous) @ react-hot-loader.development.js:3045
(anonymous) @ react-hot-loader.development.js:3004
Promise.then (async)
add @ react-hot-loader.development.js:3003
deepUpdate @ react-hot-loader.development.js:3061
Promise.then (async)
updateInstances @ react-hot-loader.development.js:3099
(anonymous) @ react-hot-loader.development.js:3113
hotSetStatus @ bootstrap:267
hotApply @ bootstrap:651
(anonymous) @ bootstrap:362
Promise.then (async)
hotUpdateDownloaded @ bootstrap:361
hotAddUpdateChunk @ bootstrap:337
webpackHotUpdateCallback @ bootstrap:57
(anonymous) @ main.9886f3792025f1ef799c.hot-update.js:1
Can also confirm. Steps to reproduce:
npx create-react-app recoil-hmr-test --template typescript
Replace the contents of App.tsx
with the following:
import React from 'react';
import {
RecoilRoot,
atom,
useRecoilState
} from 'recoil';
import './App.css';
function App() {
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});
const [text, setText] = useRecoilState(textState);
return (
<div className="App">
<RecoilRoot>
<div>{text}</div>
</RecoilRoot>
</div>
);
}
export default App;
run with yarn start
produces the error
TypeError: Cannot destructure property 'error' of 'undefined' as it is undefined.
The actual issue (see Issue #213) is a duplicate atom error.
@ericgla you should move that atom out of App
, that might prevent the error, doing this fixes it for me when I use fast-refresh
.
Additionally, if you don't need that atom to be shared, you should just use the normal React useState
.
I agree that the atom should be moved outside of App in a normal application, but I wanted to give a concise set of steps to illustrate the issue.
While moving the atom out of App
prevents the error, it doesn't solve the issue. After a bit of research the issue lies in function registerNode(node)
. This function generates a friendly message that is passed to Recoil_recoverableViolation
, but blows up since the function requires an object with a error property.
Commit 2ed203f makes the deconstructed error property optional and corrects the issue.
In the meantime, modifying line 191 in `recoil.development.js' to add a null error will fix the issue until the next release.
Recoil_recoverableViolation(message, null, { error: null });
The error message should be fixed with #199. An atom should never be defined in a render function. They need to persist across renders to refer to the same state. Creating an atom is really a side-effect that is forbidden in React render functions.
@drarmstr
I just tried it with parcel@next
which internally uses the rect hot loader
alternative React Fast Refresh
- and the result is pretty much the same. (and yes, the atom is defined in an extra js file).
Once a hot reload
happend atoms
do not update anymore 😢
I suppose once you found a way for react-hot-loader
it will also start working for React Fast Refresh
🙏
Any known fixes or workaround for this? Every code change requires manual refresh in the browser to get things working again which means clicking between VSCode and Browser -- painfully repetitive.
I also get this error (using NextJS)
Duplicate atom key "quoteSummaryTabIndex". This is a FATAL ERROR in
[1] production. But it is safe to ignore this warning if it occurred because of
[1] hot module replacement. undefined```
Need to refresh every time I save my test project Fast Refresh is not working.
Please check this sample
login.js
const { atom, selector } = require('recoil');
export const loginState = atom({
key: 'loginState',
default: {
username: '',
password: '',
completeName: ''
}
});
App.js
import React, { useCallback } from 'react';
import { RecoilRoot, useRecoilState } from 'recoil';
import { loginHandleChange } from 'Store/login';
const App = () => {
return (
<RecoilRoot>
<label>asd</label>
<Textinput />
</RecoilRoot>
);
};
function Textinput () {
const [ data, handleChange ] = useRecoilState(loginHandleChange);
return (
<form>
<input type='text' name='username' value={data.username} onChange={handleChange} />
<input type='password' name='password' value={data.password} onChange={handleChange} />
</form>
);
}
export default App;
For the error
index.js:1 Warning: Cannot update a component (`Batcher`) while rendering a different component (`Textinput`). To locate the bad setState() call inside `Textinput`, follow the stack trace as described in https://fb.me/setstate-in-render
in Textinput (at App.js:9)
in RecoilRoot (at App.js:7)
in App (at src/index.js:4)
am i doing it correctly?
I am experiencing the same duplicate atom key error:
Duplicate atom key "quoteSummaryTabIndex". This is a FATAL ERROR in
[1] production. But it is safe to ignore this warning if it occurred because of
[1] hot module replacement. undefined
I am using CRA and only started seeing the error and having HMR issues when I bumped to the latest react-scripts version. I downgraded to react-scripts@^3.4.3 while this is getting worked on and that resolved my issues for now.
This won't work for some if they are reliant on some of the new react-script features. For those that are not, a downgrade (only as a temporary solution) may do the trick if you are good with sacrificing HMR.
same problem here, could anybody help @drarmstr @csantos4242?
I don't know if react-hot-loader or react-fast-refresh can be used without webpack, but if used with webpack (eg. create-react-app) you can prevent this warning by adding module.hot.decline();
to the module where you define your atoms and selectors.
Clearly not optimal if you define an atom in every component but working great with a centralized state module.