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

Upgrade to react 18 #176

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Platforms>x64;ARM64</Platforms>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<Version>2.120.2</Version>
<Version>2.124.0</Version>
<Authors>OutSystems</Authors>
<Product>ReactView</Product>
<Copyright>Copyright © OutSystems 2023</Copyright>
Expand Down
1 change: 1 addition & 0 deletions ReactViewControl/ReactViewControl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<UseWpf>true</UseWpf>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
<Configurations>Debug;Release;ReleaseAvalonia;ReleaseWPF</Configurations>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not merge

</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'ReleaseWPF'">
Expand Down
7 changes: 4 additions & 3 deletions ReactViewResources/Loader/Bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ async function loadFramework(view: ViewMetadata): Promise<void> {
const externalLibsPath = libsPath + "node_modules/";

await loadScript(externalLibsPath + "prop-types/prop-types.min.js", view); /* Prop-Types */
await loadScript(externalLibsPath + "react/umd/react.production.min.js", view); /* React */
await loadScript(externalLibsPath + "react-dom/umd/react-dom.production.min.js", view); /* ReactDOM */

await loadScript(externalLibsPath + "react/umd/react.development.js", view); /* React */
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not merge this

await loadScript(externalLibsPath + "react-dom/umd/react-dom.development.js", view); /* ReactDOM */
define("react", [], () => window[reactLib]);
define("react-dom", [], () => window[reactDOMLib]);
define("react-dom/client", [], () => window[reactDOMLib]);
}

bootstrap();
16 changes: 16 additions & 0 deletions ReactViewResources/Loader/Internal/ComponentWithMountCallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from "react";
import { PropsWithChildren, useState, useEffect } from "react";

export interface IComponentWithMountCallbackProps {
mounted?(): void;
}

export function ComponentWithMountCallback({ mounted, children }: PropsWithChildren<IComponentWithMountCallbackProps>): JSX.Element | null {
const [isMounted, setIsMounted] = useState(false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ref... otherwise this leads to an extra render

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i actually wanted this way because I was getting a crash stating that there was a render after the hydration
this is basically avoiding 1 render to fix that hydration issue - by postponing the render in a way

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don get it... I stand with my opinion

useEffect(() => {
setIsMounted(true);
mounted?.();
}, []);

return isMounted ? <React.Fragment key={"asd"}>{children}</React.Fragment> : null;
}
5 changes: 3 additions & 2 deletions ReactViewResources/Loader/Internal/ComponentsRenderCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ export function storeViewRenderInCache(view: ViewMetadata, cacheEntry: IRenderCa
// cache view html for further use
const elementHtml = view.root!.innerHTML;
// get all stylesheets except the sticky ones (which will be loaded by the time the html gets rendered) otherwise we could be loading them twice
const stylesheets = getStylesheets(view.head!).filter(l => l.dataset.sticky !== "true").map(l => l.outerHTML).join("");


const stylesheets = getStylesheets(view.root!).filter(l => l.dataset.sticky !== "true").map(l => l.outerHTML).join("");

// the remaining code can be executed afterwards
return new Promise<void>(() => {
// insert rendered html into the cache
Expand Down
13 changes: 11 additions & 2 deletions ReactViewResources/Loader/Internal/Loader.View.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as ReactDOMClient from "react-dom/client";
import { ViewMetadataContext } from "../Internal/ViewMetadataContext";
import { PluginsContext, PluginsContextHolder } from "../Public/PluginsContext";
import { formatUrl, ResourceLoader } from "../Public/ResourceLoader";
Expand All @@ -8,6 +8,7 @@ import { notifyViewDestroyed, notifyViewInitialized } from "./NativeAPI";
import { ViewMetadata } from "./ViewMetadata";
import { ViewPortalsCollection } from "./ViewPortalsCollection";
import { addView, deleteView } from "./ViewsCollection";
import { ComponentWithMountCallback } from "./ComponentWithMountCallback";

export function createView(componentClass: any, properties: {}, view: ViewMetadata, componentName: string) {
componentClass.contextType = PluginsContext;
Expand All @@ -30,7 +31,15 @@ export function createView(componentClass: any, properties: {}, view: ViewMetada
}

export function renderMainView(children: React.ReactElement, container: Element) {
return new Promise<void>(resolve => ReactDOM.hydrate(children, container, resolve));
return new Promise<void>(resolve => {
const childrenWithRenderCallback = (
<ComponentWithMountCallback key={"ooodd"} mounted={resolve}>
{children}
</ComponentWithMountCallback>
);

ReactDOMClient.hydrateRoot(container, childrenWithRenderCallback);
});
}

function onChildViewAdded(childView: ViewMetadata) {
Expand Down
4 changes: 2 additions & 2 deletions ReactViewResources/Loader/Internal/ResourcesLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ export function loadScript(scriptSrc: string, view: ViewMetadata): Promise<void>
resolve();
});

if (!view.head) {
if (!view.root) {
throw new Error(`View ${view.name} head is not set`);
}
view.head.appendChild(script);
view.root.appendChild(script);
});
}

Expand Down
30 changes: 11 additions & 19 deletions ReactViewResources/Loader/Internal/ViewPortal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { webViewRootId } from "../Internal/Environment";
import { getStylesheets } from "./Common";
import { ViewMetadata } from "./ViewMetadata";
Expand All @@ -8,7 +9,7 @@ export type ViewLifecycleEventHandler = (view: ViewMetadata) => void;
export type ViewErrorHandler = (view: ViewMetadata, error: Error) => void;

export interface IViewPortalProps {
view: ViewMetadata
view: ViewMetadata;
viewMounted: ViewLifecycleEventHandler;
viewUnmounted: ViewLifecycleEventHandler;
viewErrorRaised: ViewErrorHandler;
Expand All @@ -27,12 +28,10 @@ interface IViewPortalState {
* A view portal is persisted until its View Frame counterpart disappears.
* */
export class ViewPortal extends React.Component<IViewPortalProps, IViewPortalState> {

private head: Element;
private shadowRoot: HTMLElement;

constructor(props: IViewPortalProps, context: any) {
super(props, context);
constructor(props: IViewPortalProps) {
super(props);

this.state = { component: null! };

Expand All @@ -56,16 +55,15 @@ export class ViewPortal extends React.Component<IViewPortalProps, IViewPortalSta
}

public componentDidMount() {
this.props.view.head = this.head;

const styleResets = document.createElement("style");
styleResets.media = "text/css";
styleResets.innerHTML = ":host { all: initial; display: block; }";

this.head.appendChild(styleResets);
this.shadowRoot.appendChild(styleResets);

// get sticky stylesheets
const stylesheets = getStylesheets(document.head).filter(s => s.dataset.sticky === "true");
stylesheets.forEach(s => this.head.appendChild(document.importNode(s, true)));
stylesheets.forEach(s => this.shadowRoot.appendChild(document.importNode(s, true)));

this.props.viewMounted(this.props.view);
}
Expand All @@ -81,15 +79,9 @@ export class ViewPortal extends React.Component<IViewPortalProps, IViewPortalSta

public render(): React.ReactNode {
return ReactDOM.createPortal(
<>
<head ref={e => this.head = e!}>
</head>
<body>
<div id={webViewRootId} ref={e => this.props.view.root = e!}>
{this.state.component ? this.state.component : null}
</div>
</body>
</>,
<div id={webViewRootId} key={"fdsdfsdf"} ref={e => this.props.view.root = e!}>
{this.state.component ? this.state.component : null}
</div>,
this.shadowRoot);
}
}
}
7 changes: 4 additions & 3 deletions ReactViewResources/Loader/Internal/ViewPortalsCollection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ interface IViewPortalsCollectionProps {
* */
export class ViewPortalsCollection extends React.Component<IViewPortalsCollectionProps> {

constructor(props: IViewPortalsCollectionProps, context: any) {
super(props, context);
constructor(props: IViewPortalsCollectionProps) {
super(props);
props.views.addChangedListener(() => this.forceUpdate());
}

Expand All @@ -28,7 +28,8 @@ export class ViewPortalsCollection extends React.Component<IViewPortalsCollectio

private renderViewPortal(view: ViewMetadata) {
return (
<ViewPortal key={view.name}
<ViewPortal
key={view.name}
view={view}
viewMounted={this.props.viewAdded}
viewUnmounted={this.props.viewRemoved}
Expand Down
5 changes: 2 additions & 3 deletions ReactViewResources/Loader/Loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,9 @@ export function loadComponent(
return; // view was probably unloaded
}

const head = view.head;
const rootElement = view.root;

if (!rootElement || !head) {
if (!rootElement) {
throw new Error(`View ${view.name} head or root is not set`);
}

Expand All @@ -158,7 +157,7 @@ export function loadComponent(

// load component dependencies js sources and css sources
const dependencyLoadPromises = dependencySources.map(s => loadScript(s, view) as Promise<any>)
.concat(cssSources.map(s => loadStyleSheet(s, head, false)));
.concat(cssSources.map(s => loadStyleSheet(s, rootElement, false)));
await Promise.all(dependencyLoadPromises);

// main component script should be the last to be loaded, otherwise errors might occur
Expand Down
3 changes: 3 additions & 0 deletions ReactViewResources/ReactViewResources.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<Configurations>Debug;Release;ReleaseAvalonia;ReleaseWPF</Configurations>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -102,10 +103,12 @@

<ItemGroup>
<EmbeddedResource Include="node_modules\react\umd\react.production.min.js" />
<EmbeddedResource Include="node_modules\react\umd\react.development.js" />
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not merge

</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="node_modules\react-dom\umd\react-dom.production.min.js" />
<EmbeddedResource Include="node_modules\react-dom\umd\react-dom.development.js" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading