Skip to content

Commit 729313a

Browse files
committed
Adds hot-reload support
1 parent 026c512 commit 729313a

File tree

10 files changed

+90
-23
lines changed

10 files changed

+90
-23
lines changed

Diff for: extras/assets/nodegui.png

39.7 KB
Loading

Diff for: extras/assets/nodegui_white.png

65.9 KB
Loading

Diff for: package-lock.json

+17-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
"docs": "typedoc"
2121
},
2222
"dependencies": {
23+
"react-deep-force-update": "^2.1.3",
24+
"react-proxy": "^2.0.8",
2325
"react-reconciler": "^0.21.0"
2426
},
2527
"peerDependencies": {
26-
"@nodegui/nodegui": "*",
28+
"@nodegui/nodegui": ">=0.3.0",
2729
"@nodegui/qode": "*",
2830
"react": "^16.9.0"
2931
},

Diff for: src/components/config.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@ import { AppContainer } from "../reconciler";
55
type UpdatePayload = any;
66

77
export interface RNProps {}
8-
export abstract class RNWidget extends NodeWidget {
8+
export abstract class RNWidget extends NodeWidget implements RNComponent {
9+
static tagName: string;
10+
abstract setProps(newProps: RNProps, oldProps: RNProps): void;
11+
abstract appendInitialChild(child: NodeWidget): void;
12+
abstract appendChild(child: NodeWidget): void;
13+
abstract insertBefore(child: NodeWidget, beforeChild: NodeWidget): void;
14+
abstract removeChild(child: NodeWidget): void;
15+
}
16+
export abstract class RNComponent {
917
static tagName: string;
1018
abstract setProps(newProps: RNProps, oldProps: RNProps): void;
1119
abstract appendInitialChild(child: NodeWidget): void;
@@ -24,25 +32,25 @@ export abstract class ComponentConfig {
2432
rootInstance: AppContainer,
2533
context: any,
2634
workInProgress: Fiber
27-
): NodeWidget;
35+
): RNComponent;
2836
finalizeInitialChildren(
29-
instance: NodeWidget,
37+
instance: RNComponent,
3038
newProps: RNProps,
3139
rootContainerInstance: AppContainer,
3240
context: any
3341
) {
3442
return false;
3543
}
3644
commitMount(
37-
instance: NodeWidget,
45+
instance: RNComponent,
3846
newProps: RNProps,
3947
internalInstanceHandle: any
4048
) {
4149
return;
4250
}
4351
// Update methods:
4452
prepareUpdate(
45-
instance: NodeWidget,
53+
instance: RNComponent,
4654
oldProps: RNProps,
4755
newProps: RNProps,
4856
rootContainerInstance: AppContainer,
@@ -51,7 +59,7 @@ export abstract class ComponentConfig {
5159
return true;
5260
}
5361
abstract commitUpdate(
54-
instance: NodeWidget,
62+
instance: RNComponent,
5563
updatePayload: any,
5664
oldProps: RNProps,
5765
newProps: RNProps,

Diff for: src/development/hot-reload/index.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import createProxy, { ReactProxyComponent } from "react-proxy";
2+
import React from "react";
3+
4+
export let appProxy: ReactProxyComponent; // need to export it so that it stays without being gc'd
5+
6+
export function hot(Component: React.ComponentType): React.ComponentType {
7+
if (appProxy) {
8+
appProxy.update(Component);
9+
} else {
10+
appProxy = createProxy(Component);
11+
}
12+
return appProxy.get();
13+
}

Diff for: src/development/hot-reload/react-proxy.d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
declare module "react-proxy" {
2+
interface ReactProxyComponent {
3+
update(Component: React.ComponentType): void;
4+
get(): React.ComponentType;
5+
}
6+
export default function createProxy(
7+
Component: React.ComponentType
8+
): ReactProxyComponent;
9+
}

Diff for: src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export { Dial } from "./components/Dial";
1313
export { SpinBox } from "./components/SpinBox";
1414
export { ScrollArea } from "./components/ScrollArea";
1515
export { useEventHandler } from "./hooks";
16+
export { hot, appProxy } from "./development/hot-reload";

Diff for: src/reconciler/index.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import Reconciler from "react-reconciler";
22
import { NodeWidget } from "@nodegui/nodegui";
3-
import { getComponentByTagName, RNWidget, RNProps } from "../components/config";
3+
import {
4+
getComponentByTagName,
5+
RNWidget,
6+
RNProps,
7+
RNComponent
8+
} from "../components/config";
49

510
export type AppContainer = Set<NodeWidget>;
611
export const appContainer: AppContainer = new Set<NodeWidget>();
@@ -9,7 +14,7 @@ const HostConfig: Reconciler.HostConfig<
914
string,
1015
RNProps,
1116
AppContainer,
12-
NodeWidget,
17+
RNComponent,
1318
any,
1419
any,
1520
any,

Diff for: src/renderer/index.ts

+26-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import reconciler, { appContainer } from "../reconciler";
2-
import { Reconciler } from "react-reconciler";
2+
import ReactReconciler, { Reconciler } from "react-reconciler";
3+
import React from "react";
34
import { NodeWidget } from "@nodegui/nodegui";
5+
import { RNComponent } from "../components/config";
6+
//@ts-ignore
7+
import deepForceUpdate from "react-deep-force-update";
48

5-
type NodeGuiReconciler = Reconciler<NodeWidget, any, Set<NodeWidget>, any>;
9+
type NodeGuiReconciler = Reconciler<RNComponent, any, Set<NodeWidget>, any>;
610

711
export type RendererOptions = {
812
onRender?: () => void;
@@ -13,15 +17,23 @@ const defaultOptions = {
1317
onRender: () => {}
1418
};
1519

16-
export const Renderer = {
17-
render(element: React.ReactNode, options?: RendererOptions) {
20+
export class Renderer {
21+
static container?: ReactReconciler.FiberRoot;
22+
static forceUpdate() {
23+
if (Renderer.container) {
24+
//@ts-ignore
25+
Renderer.container._reactInternalInstance = Renderer.container.current;
26+
deepForceUpdate(Renderer.container);
27+
}
28+
}
29+
static render(element: React.ReactNode, options?: RendererOptions) {
1830
const containerInfo = appContainer;
1931
const isConcurrent = false; //disabling since there seems to be a bug with onclick listeneres (when called without a console.log inside them)
2032
const hydrate = false;
2133

2234
const rendererOptions = Object.assign({}, defaultOptions, options);
2335

24-
const container = reconciler.createContainer(
36+
Renderer.container = reconciler.createContainer(
2537
containerInfo,
2638
isConcurrent,
2739
hydrate
@@ -30,8 +42,13 @@ export const Renderer = {
3042
rendererOptions.onInit(reconciler);
3143

3244
const parentComponent = null; // Since there is no parent (since this is the root fiber). We set parentComponent to null.
33-
reconciler.updateContainer(element, container, parentComponent, () => {
34-
rendererOptions.onRender();
35-
}); // Start reconcilation and render the result
45+
reconciler.updateContainer(
46+
element,
47+
Renderer.container,
48+
parentComponent,
49+
() => {
50+
rendererOptions.onRender();
51+
}
52+
); // Start reconcilation and render the result
3653
}
37-
};
54+
}

0 commit comments

Comments
 (0)