React.js errors when updating a component which has Bricklayer content in it
Opened this issue · 1 comments
I am unable to make React work with Bricklayer reliably. Problem happens when you mount a component which has Bricklayer content in it and update the component w/o unmounting.
Repro Steps
git clone git@github.com:tugberkugurlu/ReactJsSamples.git
cd ReactJsSamples/BricklayerRepro
git checkout -qf f8cf2f2d958e597cb9aa3129c7d5c2f045f7f3f9
yarn
npm start
Open the app on http://localhost:3000
and fire up Chrome Dev Tools as well to see the errors. The app initially loads random items and bricklayer nicely shapes them (see componentDidMount
). However, HomePage
component updates the content of ItemsList
component without unmounting it in every 3 seconds. That's why I have the destroy
and reinitialize code inside componentDidUpdate
.
However, any update that ItemsList
receives makes react upset and it fails even before getting to componentDidUpdate
and it's not able to remove any elements from the DOM and it builds up indefinitely like that.
The first error is the below one:
DOMChildrenOperations.js:67 Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.removeChild @ DOMChildrenOperations.js:67processUpdates @ DOMChildrenOperations.js:183dangerouslyProcessChildrenUpdates @ ReactDOMIDOperations.js:30processQueue @ ReactMultiChild.js:139_updateChildren @ ReactMultiChild.js:359updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129_updateRenderedComponent @ ReactCompositeComponent.js:784_performComponentUpdate @ ReactCompositeComponent.js:753updateComponent @ ReactCompositeComponent.js:670receiveComponent @ ReactCompositeComponent.js:564receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129_updateRenderedComponent @ ReactCompositeComponent.js:784_performComponentUpdate @ ReactCompositeComponent.js:753updateComponent @ ReactCompositeComponent.js:670performUpdateIfNecessary @ ReactCompositeComponent.js:578performUpdateIfNecessary @ ReactReconciler.js:163runBatchedUpdates @ ReactUpdates.js:151perform @ Transaction.js:138perform @ Transaction.js:138perform @ ReactUpdates.js:90flushBatchedUpdates @ ReactUpdates.js:173closeAll @ Transaction.js:204perform @ Transaction.js:151batchedUpdates @ ReactDefaultBatchingStrategy.js:63enqueueUpdate @ ReactUpdates.js:201enqueueUpdate @ ReactUpdateQueue.js:25enqueueSetState @ ReactUpdateQueue.js:210ReactComponent.setState @ ReactComponent.js:64(anonymous function) @ index.js:103
16DOMChildrenOperations.js:67 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.removeChild @ DOMChildrenOperations.js:67processUpdates @ DOMChildrenOperations.js:183dangerouslyProcessChildrenUpdates @ ReactDOMIDOperations.js:30processQueue @ ReactMultiChild.js:139_updateChildren @ ReactMultiChild.js:359updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129_updateRenderedComponent @ ReactCompositeComponent.js:784_performComponentUpdate @ ReactCompositeComponent.js:753updateComponent @ ReactCompositeComponent.js:670receiveComponent @ ReactCompositeComponent.js:564receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129_updateRenderedComponent @ ReactCompositeComponent.js:784_performComponentUpdate @ ReactCompositeComponent.js:753updateComponent @ ReactCompositeComponent.js:670performUpdateIfNecessary @ ReactCompositeComponent.js:578performUpdateIfNecessary @ ReactReconciler.js:163runBatchedUpdates @ ReactUpdates.js:151perform @ Transaction.js:138perform @ Transaction.js:138perform @ ReactUpdates.js:90flushBatchedUpdates @ ReactUpdates.js:173closeAll @ Transaction.js:204perform @ Transaction.js:151batchedUpdates @ ReactDefaultBatchingStrategy.js:63enqueueUpdate @ ReactUpdates.js:201enqueueUpdate @ ReactUpdateQueue.js:25enqueueSetState @ ReactUpdateQueue.js:210ReactComponent.setState @ ReactComponent.js:64(anonymous function) @ index.js:103
After that, subsequent errors are the same as below one for each update that happens every 3 seconds:
DOMChildrenOperations.js:67 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.removeChild @ DOMChildrenOperations.js:67processUpdates @ DOMChildrenOperations.js:183dangerouslyProcessChildrenUpdates @ ReactDOMIDOperations.js:30processQueue @ ReactMultiChild.js:139_updateChildren @ ReactMultiChild.js:359updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129_updateRenderedComponent @ ReactCompositeComponent.js:784_performComponentUpdate @ ReactCompositeComponent.js:753updateComponent @ ReactCompositeComponent.js:670receiveComponent @ ReactCompositeComponent.js:564receiveComponent @ ReactReconciler.js:129updateChildren @ ReactChildReconciler.js:107_reconcilerUpdateChildren @ ReactMultiChild.js:213_updateChildren @ ReactMultiChild.js:316updateChildren @ ReactMultiChild.js:303_updateDOMChildren @ ReactDOMComponent.js:960updateComponent @ ReactDOMComponent.js:780receiveComponent @ ReactDOMComponent.js:734receiveComponent @ ReactReconciler.js:129_updateRenderedComponent @ ReactCompositeComponent.js:784_performComponentUpdate @ ReactCompositeComponent.js:753updateComponent @ ReactCompositeComponent.js:670performUpdateIfNecessary @ ReactCompositeComponent.js:578performUpdateIfNecessary @ ReactReconciler.js:163runBatchedUpdates @ ReactUpdates.js:151perform @ Transaction.js:138perform @ Transaction.js:138perform @ ReactUpdates.js:90flushBatchedUpdates @ ReactUpdates.js:173closeAll @ Transaction.js:204perform @ Transaction.js:151batchedUpdates @ ReactDefaultBatchingStrategy.js:63enqueueUpdate @ ReactUpdates.js:201enqueueUpdate @ ReactUpdateQueue.js:25enqueueSetState @ ReactUpdateQueue.js:210ReactComponent.setState @ ReactComponent.js:64(anonymous function) @ index.js:103
This is what I see in this repro example. However, in my actual application, I am observing the below error on the same issue which is a bit different:
VM26259:1 Uncaught TypeError: error.stack is not a function
at eval (eval at evaluate (unknown source), <anonymous>:1:7)
at eval (webpack:///./src/components/common/formUtils.js?:17:13)(anonymous function) @ VM26259:1(anonymous function) @ formUtils.js:17
error
TypeError: Cannot read property 'getHostNode' of null
at Object.getHostNode (webpack:///./~/react/lib/ReactReconciler.js?:64:28)
at ReactCompositeComponentWrapper.getHostNode (webpack:///./~/react/lib/ReactCompositeComponent.js?:383:28)
at Object.getHostNode (webpack:///./~/react/lib/ReactReconciler.js?:64:29)
at Object.updateChildren (webpack:///./~/react/lib/ReactChildReconciler.js?:130:46)
at ReactDOMComponent._reconcilerUpdateChildren (webpack:///./~/react/lib/ReactMultiChild.js?:210:32)
at ReactDOMComponent._updateChildren (webpack:///./~/react/lib/ReactMultiChild.js?:314:31)
at ReactDOMComponent.updateChildren (webpack:///./~/react/lib/ReactMultiChild.js?:301:12)
at ReactDOMComponent._updateDOMChildren (webpack:///./~/react/lib/ReactDOMComponent.js?:942:12)
at ReactDOMComponent.updateComponent (webpack:///./~/react/lib/ReactDOMComponent.js?:760:10)
at ReactDOMComponent.receiveComponent (webpack:///./~/react/lib/ReactDOMComponent.js?:718:10)
Sample Code
Adding the code used for repro steps here just for reference. However, it has dependencies as you can see. So, it will not work as is. Look at here to make it work with the above repro steps.
import './bootswatch.less';
import 'bricklayer/dist/bricklayer.css';
import './index.scss';
import React from 'react';
import ReactDom from 'react-dom';
import Bricklayer from 'bricklayer';
import loremIpsum from 'lorem-ipsum';
import _ from 'underscore';
const ItemsList = React.createClass({
componentDidMount: function() {
this.itemsListContainerBricklayer = new Bricklayer(this.refs.itemsListContainer);
},
componentDidUpdate: function() {
this.itemsListContainerBricklayer.destroy();
this.itemsListContainerBricklayer = new Bricklayer(this.refs.itemsListContainer);
},
render: function() {
const { items } = this.props;
return <div className="search-results-container">
<div className="row">
<div className="col-xs-12">
<div className="bricklayer cards-list" ref="itemsListContainer">
{items.map(item =>
<div key={item.id} className="card">{item.content}</div>
)}
</div>
</div>
</div>
</div>;
}
});
export const guid = () => {
const s4 = () => {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
};
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
};
const getRandomItems = () => {
const count = _.random(20, 100);
return _(_.range(count)).map(i => ({
id: guid(),
content: loremIpsum()
}));
};
const HelloWorld = React.createClass({
getInitialState: function() {
return {
items: getRandomItems()
};
},
componentDidMount: function() {
this.timerId = window.setInterval(() => {
this.setState({
items: getRandomItems()
});
}, 3000);
},
componentWillUnmount: function() {
window.clearInterval(this.timerId);
},
render: function() {
return <div>
<ItemsList items={this.state.items} />
</div>;
}
});
ReactDom.render(
<HelloWorld />,
document.getElementById('app')
);
Forcing the DOM part, which Bricklayer has operated on, to be unmounted (with a hacky way 😞) workaround the issue but not a great solution: https://github.com/tugberkugurlu/ReactJsSamples/blob/9cb410ed92c8066b5d046dd976909443d5d63542/BricklayerRepro/src/index.js#L10-L58
My feeling is that Bricklayer somehow removes some things that React put in place (e.g. key of the DOM element like here, etc.) and that makes React send when it tries to remove the nodes during the Reconsilation process.