How to dynamically import a module in Javascript / ReactJs / React Native - javascript

I would like to dynamically import a module from a path importPath set via Props.
var importPath;
class MainComponent extends Component {
state = {}
render() {
// Set var importPath = "path_to_module here;"
// importPath = this.props.myModulePath
return (
<ComponentToImport myComponentPath="./ToastExample" />);
}
}
export default MainComponent;
Then :
class ComponentToImport extends Component {
ToastExample: (async () => {
await import (this.props.revPath)
})()
async sayHiFromJava() {
this.state.ToastExample.showJ('Awesome', ToastExample.SHORT);
}
render() {
return (<ToastExample />);
}
}
How can I go about this?
Thank you all in advance.
How do I attach ToastExample in import ToastExample from importPath; to await import("importPath"); so that I can return(<ToastExample />);
UPDATE
I have tried :
class ComponentToImport extends Component {
ToastExample: (async () => {
await import (this.props.revPath)
})()
async sayHiFromJava() {
this.state.ToastExample.showJ('Awesome', ToastExample.SHORT);
}
render() {
return (<ToastExample />);
}
}
but I get the error :
error: bundling failed: index.js: index.js:Invalid call at line 28: import(_this.props.myComponentPath)

I guess this is the way:
import("importPath").then(() => {
// your code
});
or
await import("importPath");
// your code
see more here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

Is it what your are looking for?
const ToastExample = await import('importPath');
EDIT: Please read the official doc to set up your webpack or Babel (https://reactjs.org/docs/code-splitting.html)
class ComponentToImport extends Component {
constructor(props) {
super(props);
this.state = { module: null };
}
componentDidMount() {
const { path } = this.props;
import(`${path}`).then(module => this.setState({ module: module.default }));
}
render() {
const { module: Component } = this.state;
return <div>{Component && <Component />}</div>;
}
}

If you want to pass component to child component one way is to pass through child props.
import myComponentPath from "./ToastExample"
<ComponentToImport>
<myComponentPath />
<ComponentToImport/>
and then
class ComponentToImport extends Component {
render() {
return (this.props.children);
}
}
May be this helps.
Thanks

Related

How would you use conditional hooks inside a React.Component class

Per documentation, Hooks cannot be used inside class components. But there are ways with higher order components: How can I use React hooks in React classic `class` component?. However this answer provided does not address the case of hooks that get called on function invocation. Take this simple Toast hook from: https://jossmac.github.io/react-toast-notifications/. I'd like to call the hook inside of a class of form:
```
class MyClass extends React.Component {
onTapButton = () => {
if(conditionTrue){
addToast('hello world', {
appearance: 'error',
autoDismiss: true,
})
}
}
render(){ ... }
}
```
There'd be no way of calling addToast without using const { addToast } = useToasts() in the class method, which would throw error.
You can use withToastManager HOC to archive that work
Here is an example
import React, { Component } from 'react';
import { withToastManager } from 'react-toast-notifications';
class ConnectivityListener extends Component {
state = { isOnline: window ? window.navigator.onLine : false };
// NOTE: add/remove event listeners omitted for brevity
onlineCallback = () => {
this.props.toastManager.remove(this.offlineToastId);
this.offlineToastId = null;
};
offlineCallback = id => {
this.offlineToastId = id;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
const { isOnline } = this.state;
if (prevState.isOnline !== isOnline) {
return { isOnline };
}
return null;
}
componentDidUpdate(props, state, snapshot) {
if (!snapshot) return;
const { toastManager } = props;
const { isOnline } = snapshot;
const content = (
<div>
<strong>{isOnline ? 'Online' : "Offline"}</strong>
<div>
{isOnline
? 'Editing is available again'
: 'Changes you make may not be saved'}
</div>
</div>
);
const callback = isOnline
? this.onlineCallback
: this.offlineCallback;
toastManager.add(content, {
appearance: 'info',
autoDismiss: isOnline,
}, callback);
}
render() {
return null;
}
}
export default withToastManager(ConnectivityListener);
For more information you can also find here

Props in other component is undefined React

I'm newbie in React and trying to build a sample search filter with data from API. Unfortunately I have problem with this code.
It's get me an error ,,Cannot read property 'filter' of undefined".
It seems to me like child component doesn't get props from parent but I declared and imported this in code.
I've tried everything what I found on the internet but nothing helps. Can someone help me out with understanding what I made wrong?
Child
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Data from './Data';
class App extends Component {
constructor() {
super();
this.state = {
search : " "
};
}
updatedSearch(event) {
this.setState(
{search : event.target.value.substr(0,15)}
)
}
render () {
console.log(this.props.names)
let filterednames = this.props.names.filter(
(name) => {
return name.toLowerCase().indexOf(this.state.
search.toLowerCase()) !== -1;
}
);
return (
<div className = "App">
<h1> Users list </h1>
<Data />
<input type = "text"
placeholder = "Search by user name"
value = {this.state.search}
onChange = {this.updatedSearch.bind(this)}
/>
<ol>
{filterednames.map(name => (
<li key={name}>{name}</li>
))}
</ol>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
export default App;
Parent
import React, { Component } from 'react';
import App from './index';
class Data extends Component {
constructor(props) {
super(props);
this.state = {
names : [],
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
//Response
.then(response => response.json())
.then(output => {
let data = output;
//names in array
let listaimion = [];
for (let index = 0; index < data.length; index++) {
listaimion.push(data[index].name)
}
this.setState({names : listaimion})
})
}
render () {
return (
<div className = "Data">
<App names = {this.state.names} />
</div>
)
}
}
export default Data;
In the parent component, App needs to be declared. Also, App looks like your entry point of your application. Seems like, you might have mixed up Child and Parent here.
Parent -
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Data from './Data';
class App extends Component() {
constructor() {
this.state = {
names : [],
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
//Response
.then(response => response.json())
.then(output => {
let data = output;
let listaimion = [];
for (let index = 0; index < data.length; index++) {
listaimion.push(data[index].name)
}
this.setState({names : listaimion});
});
}
render () {
return (
<div className = "Data">
<Data names = {this.state.names} />
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
export default App;
Child
import React, { Component } from 'react';
class Data extends Component {
constructor(props) {
super(props);
}
render() {
let filterednames = this.props.names.filter((name) => {
return name.toLowerCase().indexOf(this.state.
search.toLowerCase()) !== -1;
}
);
return (<div>{filterednames.join(',')}</div>)
}
}
The <App> component should be the parent - that is where your state should live. You would then pass this.state.names from <App> to <Data> inside the App render method. You should not import App inside Data - App should render Data.
// App.js
class App extends Component {
state = {
names: []
}
componentDidMount(){
// fetch data and when it's done use this.setState({ names: data })
}
render() {
return <Data names={this.state.names}/>
}
}
// Data.js
const Data = (props) => {
return props.names.map(() => {...your map function})
}

How to test logic in ComponenWillMount using Enzyme/Jest

I am beginner in react unit testing with enzyme/jest,
I want to test my logic inside componentWillMount method.
I want to test based on my context object whether redirect happens or not based on my business logic
class ActivateSF extends Component {
constructor(props) {
super(props);
this.className = 'ActivateSF.js'
this.state = {
messages: null,
}
}
render() {
return (
<SDPActivateInterstitialUI
context={this.props.context}
messages={this.state.messages}
/>
);
}
componentWillMount() {
let context = this.props.context
if(!context.userInfo){
return this.callIdentify(context)
}
let externalLP = ExternalLandingPageUtil.getExternalLandingPageUrl(context);
if (externalLP) {
window.location.replace(`${externalLP}`);
return;
}
if (context.userInfo)
{
console.log("user identified prior to activation flow")
if (UserInfoUtil.isSubsribedUser(context))
{
window.location = '/ac'
}
else
{
this.callPaymentProcess(context)
}
}
}
You can try beforeEach to mount and in your test you call .unmount and perform your test on it.
beforeEach(() => {
const myComponent= mount(<MyComponent myprop1={...} />);
});
describe('<MyComponent/>', () => {
it('actually unmounts', () => {
...
...
myComponent.unmount();
... Do unmount tests here
});
});
Example straight from the enzyme docs: https://airbnb.io/enzyme/docs/api/ShallowWrapper/unmount.html
import PropTypes from 'prop-types';
import sinon from 'sinon';
const spy = sinon.spy();
class Foo extends React.Component {
constructor(props) {
super(props);
this.componentWillUnmount = spy;
}
render() {
const { id } = this.props;
return (
<div className={id}>
{id}
</div>
);
}
}
Foo.propTypes = {
id: PropTypes.string.isRequired,
};
const wrapper = shallow(<Foo id="foo" />);
expect(spy).to.have.property('callCount', 0);
wrapper.unmount();
expect(spy).to.have.property('callCount', 1);

Update variable in React in class not extending component

I am trying to wrap my head around ReactJS and I am stumped with an issue where I want to be able to update the value of a local variable and return the updated value.
I've read about state and I've used that when working with React Components, however, this class is just defined as const and it doesn't extend React.Component.
Is there a different way I should be defining setting the variable?
Here is a simplified version of my code:
import React from 'react';
const WelcomeForm = ({welcome}) => {
var welcomeMsg = 'Test';
DynamicContentApi.loadDynamicContent('welcome_test').then((response) => {
// response.text has content
welcomeMsg = response.text;
}).catch(() => {
welcomeMsg = '';
});
return (
<p>{welcomeMsg}</p> // Returns 'Test'
);
};
export default WelcomeForm;
The easiest option here is to change your stateless component to a stateful component.
Stateless components are just JavaScript functions. They take in an
optional input, called prop.
Stateful components offer more features, and with more features comes more baggage. The primary reason to choose class components (stateful) over functional components (stateless) is that they can have state, that is what you want to update to re-render.
Here is what you can do:
class WelcomeForm extends React.Component {
state = {
welcomeMsg: ''
}
fetchFromApi() {
DynamicContentApi.loadDynamicContent("welcome_test")
.then(response => {
this.setState({welcomeMsg: response.text});
})
.catch((e) => console.log(e));
}
componentDidMount() {
fetchFromApi();
}
render() {
return (
<p>{welcomeMsg}</p>
);
}
};
If you want, for any reason, to keep your component stateless, you will have to put the loadDynamicContent() function on the Parent and pass the text to WelcomeForm as a prop. For example:
// Your WelcomeForm Component
const WelcomeForm = ({welcomeMsg}) => (
<p>{welcomeMsg}</p>
);
// Whatever it's Parent Component is
class Parent extends React.Component {
state = {
welcomeMsg: ''
}
fetchFromApi() {
DynamicContentApi.loadDynamicContent("welcome_test")
.then(response => {
// response.text has content
this.setState({welcomeMsg: response.text});
})
.catch((e) => console.log(e));
}
componentDidMount() {
fetchFromApi();
}
render() {
<WelcomeForm welcomeMsg={this.state.welcomeMsg} />
}
}
As suggested in the comments, you can pass the DynamicContentApi logic to outside:
import ReactDOM from 'react-dom'
DynamicContentApi.loadDynamicContent('welcome_test').then((response) => {
ReactDOM.render(<WelcomeForm data={response.text} />, document.getElementById('where you wanna render this'));
}).catch(() => {
console.log('error while fetching...');
});
And where you have your component:
import React from 'react';
export default class WelcomeForm extends React.Component {
render() {
return (
<p>{this.props.data}</p>
);
}
}

Cannot read property 'contextTypes' of undefined, react-router testing

I want to test my function which is used to verify the user login status. But I always get the error
TypeError: Cannot read property 'contextTypes' of undefined
at createMountWrapper (node_modules/enzyme-adapter-utils/build/createMountWrapper.js:147:37)
at Object.render (node_modules/enzyme-adapter-react-15/build/ReactFifteenAdapter.js:159:88)
at new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:98:16)
at mount (node_modules/enzyme/build/mount.js:19:10)
at renderedMount (src/hoc/AuthenticationHOC.test.js:20:32)
at Object.it (src/hoc/AuthenticationHOC.test.js:25:21)
at new Promise (<anonymous>)
at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
Actually I used createRouterContext to build a fake props for react-routers, and hope to test the change of this.props.history property when the page was changed. Here is the function that used to verify the user login status.
export function authenticationRequired(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
// if the user has no 'user_id' attribute, assume the user is logged out
if (!nextProps.user_id) {
this.props.history.replace('/login')
}
}
render() {
// return the given component as is
return <WrappedComponent {...this.props} />
}
}
}
And here is the current test file I created
import React from 'react'
import { shallow, mount } from "enzyme"
import { MemoryRouter, withRouter } from 'react-router-dom'
import { authenticationRequired } from './AuthenticationHOC'
import createRouterContext from 'react-router-test-context'
import sinon from 'sinon'
// dummy component to be wrapped during tests
class WrappedComponent extends React.Component {
render() {
return <div>WrappedComponent</div>
}
}
describe('AuthenticationHOC', () => {
describe('authenticationRequired', () => {
let props;
const context = createRouterContext();
const renderedMount = () => {
return mount( authenticationRequired(<WrappedComponent {...props} />), { context } )
}
const spy = sinon.spy(authenticationRequired(<WrappedComponent {...props} />).prototype, 'componentWillReceiveProps');
it('renders the wrapped component', () => {
let wrapper = renderedMount()
expect(wrapper.contains(<WrappedComponent {...props} />)).toBe(true)
})
describe("when user_id doesn't exist", () => {
beforeEach(() => {
props = {
user_id: ''
}
});
it('should go to the login page', () => {
//how to test the method componentWillReceiveProps
let wrapper = renderedMount();
wrapper.setProps({
user_id: ''
});
//expect(wrapper.instance().props().history).toBe(1)
expect(context.router.history.location.pathname).toBe('/login');
})
})
Did I miss anything? Can anyone help me solve this testing issue?
Since your testing component is not a native react-router component you should also declare add a static contextTypes property to it before testing, as explained here:
const context = createRouterContext();
const childContextTypes = {
router: React.PropTypes.object
}
const renderedMount = () => {
return mount( authenticationRequired(<WrappedComponent {...props} />), { context, childContextTypes } )
}

Categories