Skip to content

Commit

Permalink
Update react template.
Browse files Browse the repository at this point in the history
Remove mobx, use functional component, pass HTML attributes as props
  • Loading branch information
ryanelian committed Apr 6, 2021
1 parent 1b2e016 commit 897ad98
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 74 deletions.
35 changes: 18 additions & 17 deletions templates/react/client/js/components/Hello.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import React from 'react';
import { Alert } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCheckCircle } from '@fortawesome/free-solid-svg-icons';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';

@observer
export class Hello extends React.Component {
@observable isVisible = true;
export const Hello: React.FunctionComponent<{
language: string;
sdk: string;
}> = function (props) {
return (
// if using state is required, use state hook:
// https://reactjs.org/docs/hooks-state.html

// This syntax ensures `this` is bound within onDismiss.
onDismiss = (): void => {
this.isVisible = false;
}

render(): JSX.Element {
return <Alert color="success" toggle={this.onDismiss} isOpen={this.isVisible}>
<FontAwesomeIcon className="mr-3" icon={faCheckCircle}></FontAwesomeIcon>
Hello from instapack and react!
</Alert>
}
<div className="alert alert-success alert-dismissible fade show" role="alert">
<FontAwesomeIcon className="me-3" icon={faCheckCircle}></FontAwesomeIcon>
Hello from {props.sdk} and {props.language}!
</div>
);
}

Hello.propTypes = {
language: PropTypes.string.isRequired,
sdk: PropTypes.string.isRequired
};

export default Hello;
1 change: 1 addition & 0 deletions templates/react/client/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import 'ts-polyfill/lib/es2019-array';
import 'ts-polyfill/lib/es2019-object';
import 'ts-polyfill/lib/es2019-string';
import 'ts-polyfill/lib/es2020-string';
import 'bootstrap';

import './react-project';
6 changes: 6 additions & 0 deletions templates/react/client/js/react-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { renderAsyncComponent } from './react-renderer';

// use this file to render top-level components asynchronously.

// for example: allows calling <Hello sdk="instapack" language="react"></Hello> in HTML!
renderAsyncComponent('Hello', () => import('./components/Hello'));
31 changes: 0 additions & 31 deletions templates/react/client/js/react-project.tsx

This file was deleted.

68 changes: 68 additions & 0 deletions templates/react/client/js/react-renderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';

/**
* A factory function returning a Promise of React default-exported Component Class Module.
*/
type ReactAsyncComponentClassFactory = () => Promise<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
default: React.ComponentClass<any> | React.FunctionComponent<any>;
}>;

/**
* Convert a hyphenated string to camelCase.
*/
function hyphenToCamelCase(name: string) {
return name.replace(/-(.)/g, (_match: string, char: string) => {
return char.toUpperCase();
});
}

/**
* Attempts to extract element attributes as string map.
* @param el
* @returns
*/
function convertElementAttributesToPropsMap(el: Element): Record<string, string> {
if (el.hasAttributes() === false) {
return {};
}
const result: Record<string, string> = {};

for (const attribute of el.attributes) {
let name = attribute.name;
// reference: https://github.com/reactjs/react-magic/blob/3b14406a1dbd243f239d559951e03d4337d4d71f/src/htmltojsx.js#L26-L29
if (name === 'for') {
name = 'htmlFor';
}
if (name === 'class') {
name = 'className';
}
name = hyphenToCamelCase(name);
result[attribute.name] = attribute.value;
}

return result;
}

/**
* For each matching HTML Elements, render and mount a React Component asynchronously.
* Passes Element attributes as string to props.
* @param selector HTML Element selector query
* @param lazyComponent React Async Component Class Factory function
*/
export function renderAsyncComponent(selector: string, lazyComponent: ReactAsyncComponentClassFactory): void {
for (const el of document.querySelectorAll(selector)) {
const LazyComponent = React.lazy(lazyComponent);
const fallback = <FontAwesomeIcon icon={faSpinner} pulse></FontAwesomeIcon>
const props = convertElementAttributesToPropsMap(el);
const render = (
<Suspense fallback={fallback}>
<LazyComponent {...props}></LazyComponent>
</Suspense>
);
ReactDOM.render(render, el);
}
}
49 changes: 23 additions & 26 deletions templates/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,32 @@
"output": "wwwroot"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.12",
"@types/luxon": "^1.25.0",
"@types/react": "^16.9.56",
"@types/react-dom": "^16.9.9",
"@types/reactstrap": "^8.7.1",
"@types/requirejs": "^2.1.32",
"axios": "^0.21.0",
"bootstrap": "^4.5.3",
"@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-regular-svg-icons": "^5.15.3",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
"@popperjs/core": "^2.9.2",
"@types/luxon": "^1.26.3",
"@types/prop-types": "^15.7.3",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"axios": "^0.21.1",
"bootstrap": "5.0.0-beta3",
"linq": "^3.2.3",
"luxon": "^1.25.0",
"mobx": "^6.0.4",
"mobx-react": "^7.0.5",
"popper.js": "^1.16.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"reactstrap": "^8.7.1",
"sweetalert2": "^10.10.1",
"luxon": "^1.26.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sweetalert2": "^10.15.7",
"ts-polyfill": "^3.8.2",
"tslib": "^2.0.3"
"tslib": "^2.2.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4.8.1",
"@typescript-eslint/parser": "4.8.1",
"eslint": "7.13.0",
"eslint-plugin-react": "^7.21.5",
"jquery": "^3.5.1",
"typescript": "^4.1.2"
"@typescript-eslint/eslint-plugin": "4.21.0",
"@typescript-eslint/parser": "4.21.0",
"eslint": "7.23.0",
"eslint-plugin-react": "^7.23.1",
"jquery": "^3.6.0",
"typescript": "^4.2.3"
}
}

0 comments on commit 897ad98

Please sign in to comment.