Webpack require.ensure(code splitting) with react component not working? - javascript

I have the below component. It is not rendering the product module in the dom and also not showing any error in the console.
And if I use ReactDOM.render(<ProductModule/>,document.getElementById('product-container')); it is working.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
class ProductModuleWrapper extends Component {
constructor(props) {
}
render() {
return (
<div className="product-container">
{this.renderProductModules()}
</div>
);
}
renderProductModules() {
require.ensure([],(require) => {
var ProductModule = require('../ProductModule').default;
return ProductModule;
},'productmodule');
}
}
edit :
I think this is something to do with the async nature of the require ensure call, Please help

React will only re-render if component's state or props have been updated. In your example, neither is so it won't render the component as it's fetched asynchronously.
I recommend that you use this.state as described in this blog post: http://blog.netgusto.com/asynchronous-reactjs-component-loading-with-webpack/

Related

React StrictMode double initializasion - where to put logic that should be executed only once

I have a class inheriting React.Component as such :
import { Component } from "react"
import {Map} from "ol" // the goal is to render a map with openlayers
import './MapCanvas.css'
class MapCanvas extends Component {
constructor(props) {
super(props)
this.state = {}
}
componentDidMount() {
console.log('hello') // called twice
const canvas = new Map(/* map options, irrelevant here */)
this.setState({canvas})
}
componentWillUnMount () {
console.log('goodbye') // never called
if (this.state && this.state.canvas) {
this.state.canvas.setTarget(undefined) // should remove the map
}
}
render() {
return (
<div id="map" className="map"></div>
)
}
}
export default MapCanvas
My app looks like this
import MapCanvas from './components/MapCanvas'
import './App.css'
function App() {
return (
<MapCanvas/>
)
}
export default App
and index is
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './views/App';
const root = createRoot(document.getElementById('app'))
root.render(
<StrictMode>
<App/>
</StrictMode>
)
If I remove StrictMode, Everything looks fine,
However if I leave it, the componentDidMount method of MapCanvas gets called twice, and I end up having 2 maps on the page. componentWillUnmount never gets called in the process.
I read here
and there
that React StrictMode calls some functions multiple times in Developpement mode.
As far as I understood, it is to help us code in a cleaner way, but I can't understand what I am supposed to do.
My question is, where am I supposed to setup the ol.Map so that it respects the best practices and gets called only once, OR gets properly "destroyed" on update ?
edit
just as posting the question I realized I had a typo on componentWillUnmount (M instead of m)
I Corrected it, now I see "goodbye" in the console, but I still have 2 maps.
If this question is more about openlayer thant about react, let it know in the comments and I'll update or delete it
edit #2
Using class attributes instead of React state to store the canvas gives the expected result.
Is this considered good practice or is there a better way ?
Here is a workaround that worked for me:
using a class attribute instead of the React State
import { Component, createRef } from "react"
import { Map } from "ol"
class MapCanvas extends Component {
constructor(props) {
super(props)
this.canvas = new Map(/* options */)
this.ref = createRef()
}
componentDidMount() {
this.canvas.setTarget(this.ref.current)
}
componentWillUnmount () {
this.canvas.setTarget(undefined)
}
render() {
return (
<div ref={this.ref} className="map"></div>
)
}
}
export default MapCanvas
It may not be good react practice, but it solves the prolem for now
If you have a better solution you can post an answer i'll consider accepting it instead

Functional component to class component Error: Cannot read property 'id' of undefined

I'm working on a small project and am in the process of translating a functional component to a class based component so that my component can manage its state. In translating the component over, I've run into an error:
TypeError:Cannot read property 'id' of undefined
This component worked as a functional component so I'm not sure why I'm receiving this error now. Can anyone answer why in translating from a functional component to a class based one that I'm receiving this error? Is this something to do with scope at all? Do I just have some syntax wrong? I've been banging my head against this for a while and just can't understand why it cannot read the property of 'id' which is in now in the return of the render portion of my component.
Component code:
import React, {Component} from "react";
import "./OpenTasksComponent.css"
class openTasks extends Component{
constructor(){
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
let x = document.body.nodeName;
console.log(x);
}
render(){
return (
<div className="tasks" value = {this.task.id}>
<h1 onClick={this.handleClick}>{this.task.clientName}</h1>
<div className="accordion-item accordion-item-open">
<h2>{this.task.matter}</h2>
<p>{this.task.matterStatus}</p>
</div>
</div>
)
}
}
export default openTasks
App code:
import './App.css';
import Task from './components/open-tasks-component/open-tasks-component';
import tasksData from './components/open-tasks-component/tasks-data';
import Header from './Header';
function App() {
const Tasks = tasksData.map(task=> <Task key={task.id} task ={task}/>)
return (
<div className="App">
<Header />
{Tasks}
</div>
);
}
export default App;
In App you passed a prop down, but you tried to read it in the child class component using this.task.id it should be this.props.task.id
Functional components can also manage states.
You can use useState.
const [state, setState] = useState(null);

React Uncaught DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('<div>') is not a valid name

In react applications, we always have a root component and everything else is a child of that component.
So what I decided to do is break that convention whenever I am going to display a modal, and create a new element or a new component and append it directly to document.body.
a child of body we will not have anymore stacking z-index issues, so this modal will always show up 100% of the time. Or at least that was my thinking.
So I made a new component called modal.js
Inside of this modal, rather than returning a div with some fancy css styling on its children I am just going to return a no script tag which means don’t render anything like so:
import React, { Component } from 'react';
import ReactDOM from ‘react-dom’;
class Modal extends Component {
render() {
return <noscript />;
}
}
export default Modal;
So when I display the modal component its not going to display anything on the screen whatsoever. So then how do I get this modal on the screen then?
Well, I decided to do a bit of a workaround by adding in a componentDidMount() like so:
import React, { Component } from 'react';
import ReactDOM from ‘react-don’;
class Modal extends Component {
componentDidMount() {
}
render() {
return <noscript />;
}
}
export default Modal;
So whenever this component gets mounted or rendered to the screen I am going to create a new div in memory and assign it to this.modalTarget like so:
import React, { Component } from 'react';
import ReactDOM from ‘react-don’;
class Modal extends Component {
componentDidMount() {
this.modalTarget = document.createElement(‘<div>’);
}
render() {
return <noscript />;
}
}
export default Modal;
Here is the finished file:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Modal extends Component {
componentDidMount() {
this.modalTarget = document.createElement('<div>');
this.modalTarget.className = 'modal';
document.body.appendChild(this.modalTarget);
this._render();
}
componentWillUpdate() {
this._render();
}
componentWillUnmount() {
ReactDOM.unmountComponentAtNode(this.modalTarget);
document.body.removeChild(this.modalTarget);
}
_render() {
ReactDOM.render(<div>{this.props.children}</div>, this.modalTarget);
}
render() {
return <noscript />;
}
}
export default Modal;
I was expecting for this to work, maybe get an Inviolant error, but certain not:
Uncaught DOMException: Failed to execute 'createElement' on
'Document': The tag name provided ('<div>') is not a valid name.
I am not sure what is going on here.
A comment from Bravo helped me solve this. What helped me was refactoring this:
this.modalTarget = document.createElement('<div>');
to this:
this.modalTarget = document.createElement('div');
Why don't you use the React.Fragment?
You could do something like that...
const Modal = () => (
<React.Fragment>
<noscript />
</React.Fragment>
);
export default Modal;

Issue using AsyncTypeahead from react-bootstrap-typeahead package

I'm having an issue using the AsyncTypeahead from the react-bootstrap-typeahead project, where it seems like my onSearch handler is not getting called. I can see the typeahead on the page, but when I type in it, handleSearch is not being executed and I don't see any console logging. Here's a short example:
import React, {PropTypes, Component} from 'react';
import AsyncTypeahead from 'react-bootstrap-typeahead';
class CustomTypeahead extends Component {
state = { results: [] }
handleSearch(event) {
console.log("Show me what you got")
// fetch data here and set state to results in a promise
// this.setState(results)
}
render() {
return (
<div>
<AsyncTypeahead
onSearch={this.handleSearch.bind(this)}
options={this.state.results}/>
</div>
)
}
}
Any suggestions or insights are really appreciated!!!
Fixed by using:
import { AsyncTypeahead } from 'react-bootstrap-typeahead';
instead of
import AsyncTypeahead from 'react-bootstrap-typeahead';
and updating to version ^1.0.0 for react-bootstrap-typeahead

FindDOMNode flux/alt store

I'm trying to access a dom node from a store (using alt) in order to animate using velocity.js, however am only getting 'cannot read property of undefined'. Is it possible to use findDOMNode from an alt/flux store?
import React from 'react'
import alt from '../_alt';
import Actions from '../actions/actions';
import Velocity from 'velocity-animate/velocity';
import Body from '../modules/level_1/body/body1'
class Store {
constructor(){
this.bindListeners({
menuToggle: Actions.MENU_TOGGLE
});
this.menuStatus = false
}
menuToggle(){
if (!this.menuStatus){
this.menuStatus = true;
Velocity(React.findDOMNode(Body.refs.wrap),({ width: '50px' }), 50)
} else {
this.menuStatus = false;
}
}
}
export default alt.createStore(Store, 'Store');
Component:
import React from 'react';
import connectToStores from 'alt/utils/connectToStores';
import Store from '../../../stores/store'
import Actions from '../../../actions/actions';
import Styles from './body1.css';
import Hero from '../../objects/hero/full_width'
let image = ['../../../../assets/full_width1.jpg', 'image']
#connectToStores
export default class Body extends React.Component {
static getStores(){
return [Store];
}
static getPropsFromStores(){
return Store.getState();
}
render(){
return (
<div ref='wrap'>
<Hero content={image} />
</div>
);
}
}
Body is a react class, which does not have refs.
What you need is a react element (an instance of a react class) which is the "this" inside of render, componentDidMount, etc.
You will have to provide the react element to the store in some way (probably by calling menuToggle with the actual react element).
Alternatively you could use componentDidMount to set the ref on the Body class so that toggle could consume it.
A pattern that I have used with some success is to create an initialize action that takes as one of its arguments a React component. Then in componentDidMount() you can call that action, passing this as an argument. This allows your store to have a handle on that React element, as well as all of its associated properties so you can do things like React.findDOMNode(component.refs['someref']).

Categories

Resources