Skip to content

Commit

Permalink
feat(react plugin): add async linkining support to the gondel react p…
Browse files Browse the repository at this point in the history
…lugin
  • Loading branch information
jantimon committed Jan 16, 2020
1 parent 6aa926c commit b39ca5a
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 63 deletions.
92 changes: 61 additions & 31 deletions packages/plugins/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ export class DemoWidget extends GondelReactComponent {
}
```

## Component linking

It's also possible to link a gondel component to a react component without using a render method.

## Lazy loading
### Sync linking example

To download the javascript of your react widget only if the matching HTML Element is present you can use
the following pattern:
In this example the react app will be bundled into the same bundle (no splitting).

html

Expand All @@ -80,83 +82,111 @@ js
```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import React from 'react';
import {App} from './App';

@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent {
async start() {
this.App = await (import('./App')).App;
}
render(config) {
const App = this.App;
return
<App config={config} />
));
}
App = App;
}
```

### Lazy linking example (code splitting)

## Component linking
To only lazy load the javascript of your react widget if the matching
HTML Element is present, you can use the following pattern:

It's also possible to link a gondel component to a react component without using a render method.
html

### Sync linking example
```html
<div data-g-name="DemoWidget">
<script type="text/json">{ "foo":"bar" }</script>
Loading..
</div>
```

js

```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import {App} from './App';
import React from 'react';

@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent {
App = App;
App = import('./App').then(({App}) => App);
}
```

### Async linking example
### Async blocking linking example (code splitting)

You can use a async start method to lazy load a component and tell Gondel to wait until the javascript of the component has been loaded.
This will guarantee that the sync method of all Gondel components will be delayed until the react component was completely loaded.

html

```html
<div data-g-name="DemoWidget">
<script type="text/json">{ "foo":"bar" }</script>
Loading..
</div>
```

js

```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import React from 'react';

@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent {
async start() {
this.App = await import('./App').App;
this.App = (await import('./App')).App;
}
}
```


## Manipulating state

It is possible to manipulate the state inside a public method.
Initially the state is load from the html.
In the following example it would be `{theme: 'light'}`:

```html
<div data-g-name="DemoWidget">
<script type="text/json">{ "theme":"light" }</script>
Loading..
</div>
```

This initial state can be accesed inside the `GondelReactComponent` using `this.state`.
It is even possible to update the state by calling `this.setState`:

```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import {App} from './App';
import React from 'react';

const DemoApp = ({theme}) => (
<h1 className={theme === 'dark' ? 'dark' : 'light'}>
Hello World
</h1>
)

@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent<{counter: number}> {
App = App;
export class DemoWidget extends GondelReactComponent<{theme: 'light' | 'dark'}> {
App = DemoApp;

setCounter(counter: number) {
this.setState({counter})
setTheme(theme: 'light' | 'dark') {
this.setState({theme})
}
}
```

With this public method it is now possible to set the counter using
get component by using [`getComponentByDomNode`](https://gondel.js.org/docs/api.html#getcomponentbydomnode-domnode-namespace-gondelbasecomponent):
In the example above we created a public `setTheme` method which is now a public api for your react widget.
In combination with [`getComponentByDomNode`](https://gondel.js.org/docs/api.html#getcomponentbydomnode-domnode-namespace-gondelbasecomponent) it allows changing the state during runtime:


```js
getComponentByDomNode(domElement).setCounter(10)
getComponentByDomNode(domElement).setTheme('dark')
```

## Using Gondel components from react
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/react/dist/AppWrapper.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export declare class AppWrapper<TConfig> extends Component<Props<TConfig>, TConf
constructor(props: Props<TConfig>);
render(): JSX.Element | (((props: TConfig) => JSX.Element) & string) | (((props: TConfig) => JSX.Element) & number) | (((props: TConfig) => JSX.Element) & false) | (((props: TConfig) => JSX.Element) & true) | (((props: TConfig) => JSX.Element) & React.ReactNodeArray) | undefined;
}
export declare function createRenderAbleAppWrapper<TConfig>(props: Props<TConfig>): JSX.Element;
export declare function createRenderableAppWrapper<TConfig>(props: Props<TConfig>): JSX.Element;
2 changes: 1 addition & 1 deletion packages/plugins/react/dist/AppWrapper.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions packages/plugins/react/dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
* This is a plugin which allows a simplified usage of gondel together with react
*/
import { GondelBaseComponent, GondelComponent } from "@gondel/core";
import React, { ComponentLifecycle, StatelessComponent, ComponentClass } from "react";
import React, { ComponentClass, ComponentLifecycle, StatelessComponent } from "react";
declare type RenderableReactComponent<State> = StatelessComponent<Readonly<State>> | ComponentClass<Readonly<State>, any>;
export declare class GondelReactComponent<State> extends GondelBaseComponent implements ComponentLifecycle<null, State> {
static readonly AppPromiseMap: WeakMap<Promise<RenderableReactComponent<any>>, RenderableReactComponent<any>>;
_setInternalState: (config: State) => void | undefined;
App?: StatelessComponent<Readonly<State>> | ComponentClass<Readonly<State>, any>;
App?: RenderableReactComponent<State> | Promise<RenderableReactComponent<State>>;
state: Readonly<State>;
setState(state: Partial<State>): void;
constructor(ctx: HTMLElement, componentName: string);
Expand Down Expand Up @@ -61,3 +63,4 @@ export declare class GondelReactComponent<State> extends GondelBaseComponent imp
}
/** React hook to use Gondel components inside React */
export declare function useGondelComponent<TComponentType extends GondelComponent>(): readonly [(element: HTMLElement | null) => void, TComponentType | null];
export {};
30 changes: 20 additions & 10 deletions packages/plugins/react/dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/plugins/react/dist/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/plugins/react/src/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ export class AppWrapper<TConfig> extends Component<Props<TConfig>, TConfig> {
}
}

export function createRenderAbleAppWrapper<TConfig>(props: Props<TConfig>) {
export function createRenderableAppWrapper<TConfig>(props: Props<TConfig>) {
return <AppWrapper {...props} />;
}
Loading

0 comments on commit b39ca5a

Please sign in to comment.