Skip to content

Commit

Permalink
feat: extension-react adapt react 16/17 (#6339)
Browse files Browse the repository at this point in the history
* feat(react): adapt react 16/17

* test: update path alias, remove inner graph

* chore: config test env

---------

Co-authored-by: antv <[email protected]>
  • Loading branch information
Aarebecca and antv authored Sep 14, 2024
1 parent 163db70 commit ded7277
Show file tree
Hide file tree
Showing 17 changed files with 166 additions and 25 deletions.
4 changes: 2 additions & 2 deletions packages/g6-extension-react/__tests__/demos/euro-cup.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ExtensionCategory, register } from '@antv/g6';
import { ReactNode } from '@antv/g6-extension-react';
import styled from 'styled-components';
import { ReactNode } from '../../src';
import { Graph } from '../../src/graph';
import data from '../dataset/euro-cup.json';
import { Graph } from '../graph';

const Player = styled.div`
width: 100%;
Expand Down
4 changes: 2 additions & 2 deletions packages/g6-extension-react/__tests__/demos/g-node.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Graph as G6Graph, NodeData } from '@antv/g6';
import { ExtensionCategory, register } from '@antv/g6';
import { GNode, Group, Image, Rect, Text } from '../../src';
import { Graph } from '../../src/graph';
import { GNode, Group, Image, Rect, Text } from '@antv/g6-extension-react';
import { Graph } from '../graph';

register(ExtensionCategory.NODE, 'g', GNode);

Expand Down
2 changes: 1 addition & 1 deletion packages/g6-extension-react/__tests__/demos/graph.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Graph } from '../../src/graph';
import { Graph } from '../graph';

export const G6Graph = () => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { EdgeData, Element, GraphData, GraphOptions, IPointerEvent, NodeDat
import { ExtensionCategory, HoverActivate, idOf, register } from '@antv/g6';
import { Flex, Typography } from 'antd';
import { CSSProperties, useEffect, useState } from 'react';
import { Graph } from '../../src/graph';
import { Graph } from '../graph';

const { Text } = Typography;

Expand Down
4 changes: 2 additions & 2 deletions packages/g6-extension-react/__tests__/demos/react-node.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DatabaseFilled } from '@ant-design/icons';
import type { Graph as G6Graph, GraphOptions, NodeData } from '@antv/g6';
import { ExtensionCategory, register } from '@antv/g6';
import { ReactNode } from '@antv/g6-extension-react';
import { Badge, Button, Flex, Form, Input, Layout, Select, Table, Tag, Typography } from 'antd';
import { useRef, useState } from 'react';
import { ReactNode } from '../../src';
import { Graph } from '../../src/graph';
import { Graph } from '../graph';

const { Content, Footer } = Layout;
const { Text } = Typography;
Expand Down
6 changes: 3 additions & 3 deletions packages/g6-extension-react/__tests__/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render } from '@antv/g6-extension-react';
import { Alert, Flex, Select } from 'antd';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Outlet, RouterProvider, createBrowserRouter, useMatch, useNavigate } from 'react-router-dom';
import * as demos from './demos';

Expand Down Expand Up @@ -38,10 +38,10 @@ const router = createBrowserRouter([
]);

const container = document.getElementById('root')!;
const root = createRoot(container);

root.render(
render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
container,
);
2 changes: 1 addition & 1 deletion packages/g6-extension-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"build:cjs": "rimraf ./lib && tsc --module commonjs --outDir lib -p tsconfig.build.json",
"build:esm": "rimraf ./esm && tsc --module ESNext --outDir esm -p tsconfig.build.json",
"build:umd": "rimraf ./dist && rollup -c",
"ci": "run-s lint type-check build test",
"ci": "run-s lint type-check test build",
"dev": "vite",
"lint": "eslint ./src __tests__ --quiet && prettier ./src __tests__ --check",
"prepublishOnly": "npm run ci",
Expand Down
1 change: 1 addition & 0 deletions packages/g6-extension-react/src/elements/react/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { ReactNode } from './node';
export { render, unmount } from './render';

export type { ReactNodeStyleProps } from './node';
18 changes: 9 additions & 9 deletions packages/g6-extension-react/src/elements/react/node.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { HTMLStyleProps as GHTMLStyleProps } from '@antv/g';
import type { BaseNodeStyleProps, HTMLStyleProps } from '@antv/g6';
import { HTML } from '@antv/g6';
import type { FC, ReactNode as IReactNode } from 'react';
import type { Root } from 'react-dom/client';
import { createRoot } from 'react-dom/client';
import type { FC, ReactElement } from 'react';
import { render, unmount } from './render';

export interface ReactNodeStyleProps extends BaseNodeStyleProps {
/**
Expand All @@ -15,29 +14,30 @@ export interface ReactNodeStyleProps extends BaseNodeStyleProps {
}

export class ReactNode extends HTML {
private root!: Root;

protected getKeyStyle(attributes: Required<HTMLStyleProps>): GHTMLStyleProps {
return { ...super.getKeyStyle(attributes) };
}

public connectedCallback() {
super.connectedCallback();
this.root = createRoot(this.getDomElement());
// this.root = createRoot(this.getDomElement());
const { component } = this.attributes as unknown as ReactNodeStyleProps;
// component 已经被回调机制自动创建为 ReactNode
// component has been automatically created as ReactNode by the callback mechanism
this.root.render(component as unknown as IReactNode);
render(component as unknown as ReactElement, this.getDomElement());
}

public attributeChangedCallback(name: any, oldValue: any, newValue: any) {
if (name === 'component' && oldValue !== newValue) {
this.root.render((this.attributes as unknown as ReactNodeStyleProps).component as unknown as IReactNode);
render(
(this.attributes as unknown as ReactNodeStyleProps).component as unknown as ReactElement,
this.getDomElement(),
);
}
}

public destroy(): void {
this.root.unmount();
unmount(this.getDomElement());
super.destroy();
}
}
132 changes: 132 additions & 0 deletions packages/g6-extension-react/src/elements/react/render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import type * as React from 'react';
import * as ReactDOM from 'react-dom';
import type { Root } from 'react-dom/client';

// Let compiler not to search module usage
const fullClone = {
...ReactDOM,
} as typeof ReactDOM & {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?: {
usingClientEntryPoint?: boolean;
};
createRoot?: CreateRoot;
};

type CreateRoot = (container: ContainerType) => Root;

const { version, render: reactRender, unmountComponentAtNode } = fullClone;

let createRoot: CreateRoot | undefined;
try {
const mainVersion = Number((version || '').split('.')[0]);
if (mainVersion >= 18 && fullClone.createRoot) {
({ createRoot } = fullClone);
}
} catch (e) {
// Do nothing;
}

/**
* <zh/> 切换警告
*
* <en/> Toggle warning
* @param skip <zh/> 是否跳过警告 | <en/> Whether to skip the warning
*/
function toggleWarning(skip: boolean) {
const { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } = fullClone;

if (
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED &&
typeof __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED === 'object'
) {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.usingClientEntryPoint = skip;
}
}

const MARK = '__rc_react_root__';

// ========================== Render ==========================
type ContainerType = (Element | DocumentFragment) & {
[MARK]?: Root;
};

/**
* <zh/> 渲染 React 节点(React >= 18)
*
* <en/> Render React node(React >= 18)
* @param node - <zh/> React 节点 | <en/> React node
* @param container - <zh/> 容器 | <en/> Container
*/
function modernRender(node: React.ReactNode, container: ContainerType) {
toggleWarning(true);
const root = container[MARK] || createRoot!(container);
toggleWarning(false);

root.render(node);

container[MARK] = root;
}

/**
* <zh/> 使用旧的 React 渲染
*
* <en/> Use old React render
* @param node - <zh/> React 节点 | <en/> React node
* @param container - <zh/> 容器 | <en/> Container
*/
function legacyRender(node: React.ReactElement, container: ContainerType) {
reactRender(node, container);
}

/**
* <zh/> 渲染 React 节点(兼容 React 16 ~ 18)
*
* <en/> Render React node(Compatible with React 16 ~ 18)
* @param node - <zh/> React 节点 | <en/> React node
* @param container - <zh/> 容器 | <en/> Container
*/
export function render(node: React.ReactElement, container: ContainerType) {
if (createRoot) modernRender(node, container);
else legacyRender(node, container);
}

/**
* <zh/> 卸载 React 节点(React >= 18)
*
* <en/> Unmount React node(React >= 18)
* @param container - <zh/> 容器 | <en/> Container
* @returns <zh/> Promise | <en/> Promise
*/
async function modernUnmount(container: ContainerType) {
// Delay to unmount to avoid React 18 sync warning
return Promise.resolve().then(() => {
container[MARK]?.unmount();
delete container[MARK];
});
}

/**
* <zh/> 卸载 React 节点(React < 18)
*
* <en/> Unmount React node(React < 18)
* @param container - <zh/> 容器 | <en/> Container
*/
function legacyUnmount(container: ContainerType) {
unmountComponentAtNode(container);
}

/**
* <zh/> 卸载 React 节点(兼容 React 16 ~ 18)
*
* <en/> Unmount React node(Compatible with React 16 ~ 18)
* @param container - <zh/> 容器 | <en/> Container
* @returns <zh/> Promise | <en/> Promise
*/
export async function unmount(container: ContainerType) {
if (createRoot) {
// Delay to unmount to avoid React 18 sync warning
return modernUnmount(container);
}

legacyUnmount(container);
}
1 change: 0 additions & 1 deletion packages/g6-extension-react/src/graph/index.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages/g6-extension-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export {
ReactNode,
Rect,
Text,
render,
unmount,
} from './elements';

export type { GNodeStyleProps, ReactNodeStyleProps } from './elements';
3 changes: 2 additions & 1 deletion packages/g6-extension-react/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"jsx": "react-jsx",
"outDir": "lib",
"paths": {
"@antv/g6": ["../g6/src/index.ts"]
"@antv/g6": ["../g6/src/index.ts"],
"@antv/g6-extension-react": ["./src/index.ts"]
}
},
"extends": "../../tsconfig.json",
Expand Down
8 changes: 6 additions & 2 deletions packages/g6-extension-react/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"paths": {}
"paths": {
"@antv/g6": ["../g6/src/index.ts"],
"@antv/g6-extension-react": ["./src/index.ts"]
},
"skipLibCheck": true
},
"include": ["src/**/*", "__tests__/**/*"],
"include": ["src/**/*", "__tests__/**/*", "../g6/src/layouts/hierarchy.d.ts"],
"extends": "./tsconfig.json"
}
1 change: 1 addition & 0 deletions packages/g6-extension-react/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default defineConfig({
resolve: {
alias: {
'@antv/g6': path.resolve(__dirname, '../g6/src'),
'@antv/g6-extension-react': path.resolve(__dirname, './src'),
},
},
});
1 change: 1 addition & 0 deletions packages/g6/src/behaviors/drag-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
} else {
this.shadow = new Rect({
style: {
// @ts-ignore $layer is not in the type definition
$layer: 'transient',
...shadowStyle,
...positionStyle,
Expand Down

0 comments on commit ded7277

Please sign in to comment.