Can we integrate react component into Aurelia project? - reactjs

I have created one react component and build it using webpack and deployed on server. I want to integrate this component into Aurelia Project.
I tried using below npm module:
https://www.npmjs.com/package/aurelia-react-loader
In above module mentioned, component name need pass into html file. like in example my-react-component.js is passing into html file.
But my React Component is loading into root in html tag using below code:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render((
<App/>
), document.getElementById('root'));
and after running webpack module, it is creating one JavaScript file that is called index_bundle.js file. Here imported App module is main js component. It is rendering into index.html on root element via ReactDOM.
So I am not sure, How I am going to integrate React component link or url into Aurelia application?
Please let me know if you have any doubts in question. I can do explain in detail.
Thanks in advance.
Harish

Yeah, it's really easy to integrate a react component in to an Aurelia app. Check out my project where I do just that here: https://github.com/ashleygrant/aurelia-loves-react
I'm not even using the loader plugin you mentioned.
Here's how to wrap a third-party react component in an Aurelia custom element:
import React from 'react';
import ReactDOM from 'react-dom';
import ReactDatePicker from 'react-datepicker';
import { noView, bindable, inject } from 'aurelia-framework';
#noView(['react-datepicker/react-datepicker.css'])
#inject(Element)
export class DatePicker {
#bindable selectedDate;
#bindable onChange;
constructor(element) {
this.element = element;
}
selectedDateChanged() {
this.render();
}
render() {
ReactDOM.render(
<ReactDatePicker
selected={this.selectedDate}
onChange={date => this.onChange({ date: date })}
/>,
this.element
);
}
// How to use native DOM events to pass the changed date
/*render() {
ReactDOM.render(
<ReactDatePicker
selected={this.selectedDate}
onChange={date => {
let event = new CustomEvent('change', { 'detail': date });
// Dispatch the event.
this.element.dispatchEvent(event);
}
}
/>,
this.element
);
}*/
}
And here's how to do it while using a custom react component that is part of the Aurelia project:
import React from 'react';
import ReactDOM from 'react-dom';
import { noView, bindable, inject } from 'aurelia-framework';
import FormattedDate from '../react-components/formatted-date';
#noView()
#inject(Element)
export class ReactDate {
#bindable date;
#bindable format = 'dddd, MMMM D, YYYY';
constructor(element) {
this.element = element;
}
dateChanged() {
this.render();
}
formatChanged() {
this.render();
}
render() {
ReactDOM.render(
<FormattedDate
date={this.date}
format={this.format}
/>,
this.element
);
}
}
As you can see, it's pretty simple. I like doing it by hand rather than using the loader as I can set up databinding for each property so it works in a more "Aurelia-ey" way.

Related

Using a React component for part of a custom JS application - How to clean up?

I have recently started to use React for specific parts of my custom JavaScript application. It is going well, but I don't quite understand how I can "unmount" or "stop rendering" or "clean up" a React component when I no longer need it?
Consider the following example of opening a modal that is a React component. How do I then close it and clean up the React side of things properly?
MyApp.js (JavaScript only, no React)
import { renderReactModal } from "./ReactModal.jsx";
class MyApp {
// I call this when I want to show my React component
openReactModal() {
// Create an empty div and append it to the DOM
this.modalDomElem = document.createElement("div");
document.append(this.modalDomElem);
// Render the React component into the div
renderReactModal(this.modalDomElem);
}
// I call this method when I want to hide my React component
closeReactModal() {
// Is it enough to do this to unmount / stop the React component from rendering?
// Or is there any other React-specific clean-up code required?
this.modalDomElem.remove();
}
}
ReactModal.jsx (React goes here)
import React from "react";
import ReactDOM from "react-dom";
class ReactModal extends React.Component {
render() {
return <h2>React Modal</h2>
}
}
export const renderReactModal = (domElem) => {
// NB: This syntax is for React 16 (different in 18).
ReactDOM.render(
<ReactModal />,
domElem
);
}
I searched some more and eventually found my way to this section of the React Docs: https://reactjs.org/docs/react-dom.html#unmountcomponentatnode
It seems that using ReactDOM.unmountComponentAtNode(container) is the recommended way to achieve this:
Remove a mounted React component from the DOM and clean up its event handlers and state.
Using that idea, I can change my initial code as follows.
MyApp.js (JavaScript only, no React)
import { mountReactModal, unmountReactModal } from "./ReactModal.jsx";
class MyApp {
// I call this method when I want to show my React component
openReactModal() {
// Create an empty div and append it to the DOM
this.modalDomElem = document.createElement("div");
document.append(this.modalDomElem);
// Mount the React component into the div
// NB: This causes the React component to render
mountReactModal(this.modalDomElem);
}
// I call this method when I want to hide my React component
closeReactModal() {
// Unmount the React component from the div
// NB: This cleans up the React component's event handlers and state
unmountReactModal(this.modalDomElement);
// Remove the div from the DOM
this.modalDomElem.remove();
}
}
ReactModal.jsx (React goes here)
import React from "react";
import ReactDOM from "react-dom";
class ReactModal extends React.Component {
render() {
return <h2>React Modal</h2>
}
}
// Mount
export const mountReactModal = (domElem) => {
// NB: This syntax is for React 16 (different in 18).
ReactDOM.render(
<ReactModal />,
domElem
);
}
// Unmount
export const unmountReactModal = (domElem) => {
// NB: This syntax is for React 16 (different in 18).
ReactDOM.unmountComponentAtNode(domElem);
}

systemjs to load react function component use hooks run error。

I use webpack to bundle a react function component use useState and set output.libraryTarget: system。
import React, { useState } from "react";
const Foo = () => {
const [str] = useState("workd");
return <div>hello, {str}</div>;
};
export default [Foo];
In other react project I use systemjs to load the above component lik blow
<script type="systemjs-importmap">
{
"imports": {
"baseC": "http://localhost:8000/static/js/bundle.js"
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
<script>
System.import("baseC");
</script>
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
window.System.import("baseC").then((res) => {
const Components = res.default[0];
console.log("Components", Components);
ReactDOM.render(
<React.StrictMode>
<Components />
</React.StrictMode>,
document.getElementById("root")
);
});
then I get the error
react.development.js:1476 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
and the whole demo in https://github.com/ytudt/react-systemjs
what should I do to solve this problem? thanks.

Accessing context API when using inertia.js and code splitting

I wrote my project in react and now I want to add it to Laravel using inertia.js but I can't access context since I'm using dynamic-import for code splitting.
I'm wondering if there is a way to access context in this situation.
This is my app.js code:
import React from 'react'
import { render } from 'react-dom'
import { createInertiaApp } from '#inertiajs/inertia-react'
createInertiaApp({
resolve: name => require(./src/screens/${name}),
setup({ el, App, props }) {
render(<App {...props} />, el)
},
})

error in displaying pdf in react-pdf

I am new new to react, I am trying to display a pdf file on browser. I am getting an error as failed to load PDF. I am trying to run the sample program given in https://www.npmjs.com/package/react-pdf.
App.js
import React, { Component } from 'react';
import { Document, Page } from 'react-pdf';
class MyApp extends Component {
state = {
numPages: null,
pageNumber: 1,
}
onDocumentLoad = ({ numPages }) => {
this.setState({ numPages });
}
render() {
const { pageNumber, numPages } = this.state;
return (
<div>
<Document
file="./1.pdf"
onLoadSuccess={this.onDocumentLoad}
>
<Page pageNumber={pageNumber} />
</Document>
</div>
);
}
}
export default MyApp;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
Error screenshot
Question is old but hope this help someone. I faced this issue and found a solution here https://github.com/wojtekmaj/react-pdf/issues/321.
import { Document, Page, pdfjs } from 'react-pdf';
Add this to your constructor.
constructor(props){
super(props);
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
}
You load the file using file="./1.pdf" I believe that might be the problem.
If you have a file structure like:
src
App.js
components
ShowPdfComponent.js
1.pdf
public
bundle.js
Then you need to move the 1.pdf to public folder like this:
src
App.js
components
ShowPdfComponent.js
public
bundle.js
1.pdf
Because when your compiled javascript code is being executed from public/bundle.js and bundle.js does not know how to get to src/components/1.pdf in file system.
There might be also a difference between production/development environment if you are using webpack and webpack-dev-server.
Look at react-pdf example. It has flat file structure. That is the reason why it works.
if you are using create-react-app then you need to import differently like
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack'
because it uses webpack under the hood.
You can import the pdf file using import samplePdf from './1.pdf' and can use directly like file={samplePdf} in your Document tag.
Here is the minimum setup you need to be able to display your pdf in react TypeScript:
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist#${pdfjs.version}/legacy/build/pdf.worker.min.js`;
const YourComponentReact = ({url}: {url: string}) => {
return (<Document file={url}>
<Page pageNumber={1} />
</Document>)
}
You can add a quick button if you want to be able to change pages
You only need to download react-pdf and #types/react-pdf if you use typescript.
adding pdfjs to import and using it like this hellped
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist#${pdfjs.version}/legacy/build/pdf.worker.min.js`;
in the same file i need to show my pdf

Using React in Web Components + CSS

I am trying to implement web components using React. The reason I do this, is because we are a big newspaper company and our 3rd party CMS only allows for extendability using web components. So I read about a way to make a react component a web component on the official docs, like so:
const proto = Object.create(HTMLElement.prototype, {
attachedCallback: {
value: function() {
const mountPoint = document.createElement('span');
this.createShadowRoot().appendChild(mountPoint);
const name = this.getAttribute('name');
const url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
ReactDOM.render(<a href={url}>{name}</a>, mountPoint);
}
}
});
document.registerElement('x-search', {prototype: proto});
This actually does work. I created a react app using create-react-app and rewrote the index.js like so:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
// ReactDOM.render(<App />, document.getElementById('root'));
const proto = Object.create(HTMLElement.prototype, {
attachedCallback: {
value: function() {
const mountPoint = document.createElement('span');
this.createShadowRoot().appendChild(mountPoint);
ReactDOM.render(<App />, mountPoint);
}
}
});
document.registerElement('x-my-app', {prototype: proto});
registerServiceWorker();
And yes—this does actually work! However, what this does, is put my CSS in a separate file and as my web component has shadow DOM it is not styled at all. It needs to live inside the generated web component, but how do I do that?

Resources