Add an external child in React - reactjs

I need to add another react component that's located in an external CDN as a child in my react component. Something like this:
var RemoteComponent = require('//cdn.com/some-remote-component');
var MyComponent = React.createClass({
render: function() {
return (
<div>
<RemoteComponent />
</div>
);
}
});
module.exports = MyComponent;
Any ideas on how could I do that?

Assuming the component registers to a global you could use a script loader such as script.js. Note that it is an asynchronous process so that will lead to some complexity. You may need to run the component through React.createFactory before plugging it in dynamically.
I expect you'll need to do something like this (not tested):
export default MyComponent {
constructor(props) {
super(props);
this.state = {
component: null
};
$script('cdn url goes here', () => {
// expects to find a global named `RemoteComponent` now
this.setState({
component: React.createFactory(RemoteComponent);
})
});
}
render() {
const component = this.state.component;
return (
<div>
{component}
</div>
);
}
}
You may need to tweak the code to your liking but I hope it illustrates the basic idea.

You can also build your own function to load remote react components without using requirejs or scriptjs. It can be very straightforward as follows (I was inspired by the solution here)
import React from 'react';
export function loadRemoteComponent(url) {
return fetch(url)
.then(response => response.text())
.then(text => {
// Define exports and require method for eval(text)
const exports = {};
function require(name) {
if (name === 'react') {
return React;
}
throw new Error('Does not support modules other than "react"');
}
eval(text);
return exports.__esModule ? exports.default : exports;
});
}

Related

Pass through component functions in an external library

I have a problem. I want to use an external library within my app component. When initializing the external library, I can also tell it what to do when an action is triggered. Unfortunately, I don't know how to execute the component functions inside the external library.
Here is the stripped down code:
import React from 'react';
import axios from 'axios';
import PullToRefresh from 'pulltorefreshjs';
class App extends React.Component {
constructor(props) {
super();
this.loadAjaxData = this.loadAjaxData.bind(this);
}
componentDidMount() {
this.loadAjaxData();
PullToRefresh.init({
mainElement: 'main',
onRefresh() {
this.loadAjaxData();
}
},
});
}
loadAjaxData(){
axios.get("https://localhost/api/").then(response => {
const data = response.data;
console.log(data);
});
}
render() {
return (
<>
<Header/>
<main>
<h2>Headline</h2>
<p>Conten</p>
</main>
</>
)
}
}
export default App;
I get an error when the loadAjaxData() function is called.
Uncaught TypeError: this.loadAjaxData is not a function
at Object.onRefresh (eval at hmrApply (HMRRuntime.js:244), <anonymous>:46:16)
at index.umd.js:232
Does anyone know a solution? I already read the article "Integrating with Other Libraries" on the Reactjs website, but unfortunately that still doesn't get me anywhere.
Thank u!
Change your componentDidMount to :
componentDidMount() {
const that = this ;
that.loadAjaxData();
PullToRefresh.init({
mainElement: 'main',
onRefresh() {
that.loadAjaxData(); // <-- this line need that
}
},
});
}

React - what are the steps to get data from api and render it?

I am building a site just like stackoverflow.com. I want my home page to display top questions. For that, I have sample questions on the backed. Now, I want to display only the question and tags from the questions array.
The code is in the image
I have made axios connection for that:
const instance = axios.create({
baseURL: "https://2w2knta9ag.execute-api.ap-south-1.amazonaws.com/dev", });
instance.defaults.headers.post["Content-Type"] = "application/json";
To connect it, I wrote the command: instance.get("/questions)
Now, how do I display only the question and tags??
EDIT:
On using the code given bellow, my js file now becomes:
import React from 'react';
import instance from '../../api';
class QuestionList extends React {
componentDidMount() {
instance
.get("/questions")
.then((res) => {
this.setState({ data: res.data });
});
}
render () {
const { data } = this.state;
return <div>
{
data && data.map(d => {
return <div>question: {d.question}, tags: {d.tags}</div>;
})
}
</div>
}
}
export default QuestionList;
But, this is just making my site in a loading state, and it gets hanged!!
If I understood correctly, you want to get an array only with the tags and the question. if so, you can use Array.prototype.map for this
const questions = result.map(({ question, tags }) => ({ question, tags }))
First you export the axios instance so that it can be used from other components.
Now you can send the api request in componentDidMount and update your component's state with the data.
And in render function, you just get the value from state and display.
If you are new to react, learn React Hooks and know that componentDidMount method is the best place to send api requests.
For Example:
import React from 'react';
import instance from '../../api';
class QuestionList extends React.Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
instance.get('/questions').then((res) => {
this.setState({ data: res.data });
});
}
render() {
const { data } = this.state;
return (
<div>
{data &&
data.map((d) => {
return (
<div>
question: {d.question}, tags: {d.tags}
</div>
);
})}
</div>
);
}
}
export default QuestionList;

Best way to sharing function in react

I'm new in react. I wonder what is the best option for sharing functions (except inheritance). In inheritance way I can do something like this:
class BaseForm {
updateForm() => {};
}
class MyFormOne extends BaseForm {
// this.updateForm();
}
And I can code few Form components which extends BaseForm and I can use updateForm method in every. What is the best way in react to do this?
There are multiple ways for the question :
1) If the shared fnc is the static function (independent with the component). You should create a common file utils.js and then export, import function wherever you want
// utils.js
export function updateForm() { }
// MyformOne.js
import {updateForm} from './utils';
// Use fnc updateForm() in MyFormOne component
2) If the shared fnc is dependent with class component, you should use the design pattern in React : HOC (Higher Order Component) or render Props for sharing code (states, functions) between React components (render your code more abstract => a good practice for reusable)
class BaseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
a1 : "",
a2 : "",
}
}
// example : share the func updateForm of Baseform
updateForm = () => {}
render() {
return (
<div>
{/*
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state, this.updateForm)}
</div>
);
}
}
class WithBaseForm extends React.Component {
render() {
return (
<div>
<BaseForm render={(stateBaseForm, updateForm) => (
<MyFormOne stateBaseForm={stateBaseForm} updateForm={updateForm}/> // so MyFormOne could use the fnc updateForm of BaseForm
)}/>
</div>
);
}
}
You should view the link for more details :
https://en.reactjs.org/docs/render-props.html
https://en.reactjs.org/docs/higher-order-components.html
You can create a utils.js file and place your common JS functions there. Something like this:
export const updateFormCommon = (inputValue) => {
// Your function logic here
return something;
};
export const showMessage = () => {
alert('hello world');
};
Now you can import utils.js wherever you need and call your common\shared functions like this:
import {updateFormCommon, showMessage} from './utils'
let result = updateFormCommon(1000)}
showMessage();

Error, when dynamically import a react js module with webpack

I want to dynamically import a react js module. The module is not component it is an object of data, so I cannot use react code splitting. In the webpack docs there is an example with a promise. When I use it like that in a react component, it throws an error because the component tries to render before the promise hadd been resolved. I want to import it in that way in case the data does not exist, I could provide default data.
const dataProps = import(`./dataObject.js`).then(data=> data);
...
render() {
<SomeComponente data={data} />
}
I think the best way to do this is something like:
const dataProps = import(`./dataObject.js`); // Start the importing
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state {
data: null
}
}
componentDidMount() {
dataProps.then(data => this.setState({ data });
}
render() {
if (this.state.data !== null) {
return <SomeComponent data={this.state.data} />
}
return null;
}
}
This way when the import is done only then will you actually render anything

Webpack theme loader

I'd like to accomplish the following structure:
button.core.jsx
button.theme-a.jsx
button.theme-b.jsx
To take React as an example, I'd like to do the following in button.core.jsx:
import React from 'react';
import Themed from './button.[theme]';
export default class Button extends React.Component {
render() {
if (Themed) {
return <Themed />;
}
return <button>default button</button>;
}
}
In other words, I want to define a theme in my webpack.config.js and load that file if it exists. If it does't, render the default behaviour. I think this would be a very powerful setup!
I've been searching around for making a custom loader, but no success yet. Can anyone point me in the right direction?
I've got this working with writing a custom "resolver":
const ThemeResolver = {
apply: function(resolver) {
resolver.plugin('file', function(req, cb) {
if (req.request.indexOf('[theme]') == -1) {
return cb();
}
const defaultFile = req.request.replace('[theme]', 'Default');
const themedFile = req.request.replace('[theme]', process.env.THEME);
req.request = themedFile;
this.doResolve(['file'], req, (err) => {
if (!err) {
return cb();
}
req.request = defaultFile;
this.doResolve(['file'], req, cb);
})
});
}
};
module.exports = {
// ...
plugins: [
new webpack.ResolverPlugin([ThemeResolver]),
]
// ...
};
It tries to resolve a file with [theme] in its path into a path with the theme defined as a environment variable. If it fails, it'll fallback to a default file instead. This way I can require a themed file like so:
import Presentation from './button-[theme]'
The main component turned out to be a bit different than in my question, but I'm actually pretty content with it:
import React from 'react';
import Presentation from './button-[theme]';
export default class Button extends React.Component {
onClick = (e) => console.log('some logic');
render() {
return <Presentation onClick={ this.onClick } />;
}
}
The logic of this button-component can live inside of button.core.jsx, while the presentation will be handled by one of these components:
THEME=theme-a npm start // button-[theme] resolves to button-theme-a.jsx
THEME=theme-c npm start // button-[theme] resolves to button-default.jsx
Disclaimer: I didn't use this in a large scale or production environment yet, but it seems to work in a small POC. Please let me know if I'm doing something unwise!

Resources