React version: 17.0.1
React Reconciler version: 0.26.1
[email protected] to [email protected] and from [email protected] to [email protected]Link to code example: Unfortunately project is private, but I am including all related files below (click sections to expand)
Custom reconciler hostconfig (click to expand)
```ts
import danteApp from '$dante/util/danteApp';
import { diffProps, instances } from '$dante/util/helpers';
import { DanteInstance, InstanceType, PixiStage, UnknownProps } from '$dante/util/types';
import { isFunction } from 'lodash';
import { ReactNode } from 'react';
import ReactReconciler from 'react-reconciler';
const reconciler = ReactReconciler({
createInstance
// @ts-expect-error
return new instancestype;
},
createTextInstance() {
throw new Error('dante does not support text instances. Use Text component instead.');
},
shouldSetTextContent() {
return false;
},
appendChild(parentInstance: DanteInstance, child: DanteInstance) {
parentInstance.addDanteChild(child);
},
appendChildToContainer(container: PixiStage, child: DanteInstance) {
container.addChild(child.instance);
},
appendInitialChild(parentInstance: DanteInstance, child: DanteInstance) {
parentInstance.addDanteChild(child);
},
insertBefore(parentInstance: DanteInstance, child: DanteInstance, beforeChild: DanteInstance) {
if (child === beforeChild) {
throw new Error('dante cannot insert node before itself');
}
const index = parentInstance.getDanteChildIndex(beforeChild);
const childExists = parentInstance.getDanteChildIndex(child) !== -1;
if (childExists) {
parentInstance.setDanteChildIndex(child, index);
} else {
parentInstance.addDanteChildAt(child, index);
}
},
insertInContainerBefore(container: PixiStage, child: DanteInstance, beforeChild: DanteInstance) {
if (child === beforeChild) {
throw new Error('dante cannot insert node before itself');
}
const index = container.getChildIndex(beforeChild.instance);
const childExists = container.getChildIndex(child.instance) !== -1;
if (childExists) {
container.setChildIndex(child.instance, index);
} else {
container.addChildAt(child.instance, index);
}
},
removeChild(parentInstance: DanteInstance, child: DanteInstance) {
parentInstance.removeDanteChild(child);
},
removeChildFromContainer(container: PixiStage, child: DanteInstance) {
container.removeChild(child.instance);
child.instance.destroy();
},
getPublicInstance(instance: DanteInstance) {
return instance;
},
getRootHostContext(rootContainerInstance) {
return rootContainerInstance;
},
getChildHostContext() {
return {};
},
prepareForCommit() {
return null;
},
prepareUpdate(_instance, _type, oldProps: UnknownProps, newProps: UnknownProps) {
return diffProps(oldProps, newProps);
},
commitUpdate(instance: DanteInstance, updatePayload: UnknownProps) {
if (isFunction(instance.applyProps)) {
instance.applyProps(updatePayload);
}
},
finalizeInitialChildren(parentInstance: DanteInstance, _type, props: UnknownProps) {
if (isFunction(parentInstance.finalizeChildren)) {
parentInstance.finalizeChildren(props);
}
return false;
},
now: window.performance.now,
setTimeout: window.setTimeout,
clearTimeout: window.clearTimeout,
resetAfterCommit() {
// Noop
},
// @ts-expect-error
clearContainer() {
return false;
},
hideInstance(instance: DanteInstance) {
instance.instance.renderable = false;
instance.instance.visible = false;
},
unhideInstance(instance: DanteInstance) {
instance.instance.renderable = true;
instance.instance.visible = true;
},
noTimeout: -1,
isPrimaryRenderer: true,
supportsMutation: true,
supportsPersistence: false,
supportsHydration: false
});
export function render(root: ReactNode) {
const container = reconciler.createContainer(danteApp.stage, false, false);
reconciler.updateContainer(root, container, null, () => null);
}
export const danteRenderer = danteApp.renderer;
</details>
<details>
<summary>Hook related to the error (click to expand)</summary>
```ts
import { FONTS } from '$app/util/assets';
import theme from '$app/util/theme';
import { widthPercentage } from '$core/helpers';
import { BitmapFont, TextStyle } from 'pixi.js';
import { useCallback, useEffect, useState } from 'react';
/**
* Utilities
*/
const { fonts } = document;
const fontSize = widthPercentage(theme.fontSize.large);
const fontOptions = {
chars: BitmapFont.ALPHANUMERIC,
resolution: devicePixelRatio,
padding: widthPercentage(2)
};
const gold = new TextStyle({
fill: theme.gradient.gold,
fillGradientStops: [0.1, 0.5, 1],
fillGradientType: 0,
fontSize,
...theme.shadow.textShadowDark
});
/**
* Hook
*/
function useFonts() {
const [ready, setReady] = useState(false);
const loadFonts = useCallback(async () => {
// Load custom fonts
const openSans = new FontFace('OpenSans', `url(${FONTS.OpenSans})`, {
weight: 'normal',
style: 'normal'
});
fonts.add(openSans);
await openSans.load();
// Create optimised bitmap fonts
BitmapFont.from('Script-gold', { ...gold, fontFamily: 'OpenSans' }, fontOptions);
setReady(true);
}, []);
useEffect(() => {
loadFonts();
}, [loadFonts]);
return ready;
}
export default useFonts;
Hook usage in the application (click to expand)
import LayoutRouteTransition from '$app/components/LayoutRouteTransition';
import Route from '$app/components/Route';
import useFonts from '$app/hooks/useFonts';
import Game from '$app/layouts/Game';
import Registration from '$app/layouts/Registration';
import React, { Fragment } from 'react';
function App() {
const fonts = useFonts();
if (!fonts) {
return null;
}
return (
<LayoutRouteTransition>
{layout => (
<Fragment>
<Route route={layout} match="Registration" Component={Registration} />
<Route route={layout} match="Game" Component={Game} />
</Fragment>
)}
</LayoutRouteTransition>
);
}
export default App;
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
at resolveDispatcher (react.development.js:1476)
at useState (react.development.js:1507)
at useFonts (useFonts.ts:39)
at App (App.tsx:10)
at renderWithHooks (react-reconciler.development.js:6412)
at mountIndeterminateComponent (react-reconciler.development.js:9238)
at beginWork (react-reconciler.development.js:10476)
at HTMLUnknownElement.callCallback (react-reconciler.development.js:12184)
at Object.invokeGuardedCallbackDev (react-reconciler.development.js:12233)
at invokeGuardedCallback (react-reconciler.development.js:12292)
As far as I can tell this should be valid?
Sorry, we can't really guess without a reproducing example, but this error usually means one thing — two copies of React.
More concretely, it usually means that react resolved from your component code is a different version/copy than react resolved from the reconciler.
Hi, I hope you are well. I am not sure how I ended up on this subscribe
list but can you please remove me? Thank you!
On Fri, Oct 23, 2020 at 7:56 AM Dan Abramov notifications@github.com
wrote:
More concretely, it usually means that react resolved from your component
code is a different version/copy than react resolved from the reconciler.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/facebook/react/issues/20082#issuecomment-715296698,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAWUWA2JRUSU34FZMK63H2TSMFVQBANCNFSM4S4KBTNQ
.>
David Webb
Cell: 860.248.5632
Sent from iPhone
@nlaffey We can't subscribe or unsubscribe you, but the part you quoted contains an unsunscribe link so you can press it?
There's a good note in the React docs about a workaround with Yarn resolutions. That fixed this for me!
E.g. adding this to package.json, forcing packages to resolve to newer versions of React:
{
"resolutions": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
}
Seems like there's no issue here on our side.
I wonder if now that we made peerDependencies stricter (exact version), npm started to fail deduping during upgrades, resulting in duplicates.
@gaearon I was using react-reconciler in yarn workspace package separate from my app project. I had react: "*" in reconcilers package as a peerDep, setting that to exact version of react (17.0.1) resolved this.
Most helpful comment
More concretely, it usually means that
reactresolved from your component code is a different version/copy thanreactresolved from the reconciler.