Reputation: 2383
I have a component which includes another component in ReactJS like so:
var Child = require('Child');
var Parent = React.createClass({
render: function() {
return (
<div>
<Child key={someKey} data={someData} />
</div>
);
},
});
module.exports = Parent;
Then In my child component I want to include both the parent and child again. Sort of like nesting divs in each other.
Code for the child component below:
var Parent = require('Parent');
var Child = React.createClass({
render: function() {
return (
<div>
<Parent key={someOtherKey} data={someOtherData} />
</div>
);
}
});
module.exports = Child;
Although I'm getting the following error: Uncaught TypeError: e.toUpperCase is not a function
Is this behaviour allowed in React? If not, how is it supposed to be structured?
Upvotes: 2
Views: 585
Reputation: 4238
The first problem is that you can't import both modules into each other. The second problem is that you need to check whether or not to create the second parent object otherwise you will get an infinite loop. Something like this:
var Child = require('Child');
var Parent = React.createClass({
render: function() {
return (
<div>
<Child key={someKey} data={someData} />
</div>
);
},
});
module.exports = Parent;
And then:
var Child = React.createClass({
render: function() {
var parent = nil;
if ({hasNestedParent}) {
var Parent = require('Parent');
parent = (
<Parent key={someOtherKey} data={someOtherData} />
);
}
return (
<div>{parent}</div>
);
}
});
module.exports = Child;
Notice that the Parent
module is not imorted in the Child module unless it's needed.
Upvotes: 0
Reputation: 92150
What you want to do is ok but you have to take care of not creating an infinite recursion, so you need a stop condition.
Here is a JsFiddle executable example
function getOffset(depth) {
var prefix = "";
for (var i=0;i<depth;i++) {
prefix = prefix + "-";
}
return prefix;
}
var Parent = React.createClass({
render: function() {
var offset = getOffset(this.props.depth);
return <div>{offset}Parent=[<Child depth={this.props.depth}/>{offset}]</div>;
}
});
var Child = React.createClass({
render: function() {
if ( this.props.depth >= 5 ) {
return false;
}
var newDepth = this.props.depth +1;
var offset = getOffset(this.props.depth);
return <div>{offset}Child=(<Parent depth={newDepth}/>{offset})</div>;
}
});
React.render(<Parent depth={0} />, document.body);
The output is
Parent=[
Child=(
-Parent=[
-Child=(
--Parent=[
--Child=(
---Parent=[
---Child=(
----Parent=[
----Child=(
-----Parent=[-----]
----)
----]
---)
---]
--)
--]
-)
-]
)
]
This is funny :)
Upvotes: 0
Reputation: 4945
Here is a recursive pattern with getTreeNode as the shared element.
import React, {Component} from 'react';
import lodash from 'lodash';
var TreeRootSty = {lineHeight: '120%'}
var liSty = {listStyleType: 'none'};
var ulSty = {height: 'inherit', WebkitPaddingStart: '16px'};
var ulStyle = {height: 'inherit', WebkitPaddingStart: '16px'};
var iconSty = {marginRight: '10px', width: '16px'};
var titleSty = {color: '#afac87', marginTop: '2px'};
var nottogglable = {
color: '#FFF',
cursor: 'pointer',
margin: '0 0 0 .8em'
};
var togglable = {
color: '#815C7C',
cursor: 'pointer',
margin: '0'
};
var options = {};
var getTreeNode = function(child, index) {
return <li key={index} style={liSty}><JTreeViewNode node={child} iconClick={this.props.iconClick} titleClick={this.props.titleClick} /></li>;
};
class JTreeViewNodeRender extends Component {
binder(...methods) { methods.forEach( (method) => this[method] = this[method].bind(this) ); }
render() {
var childNodes;
var pSty = nottogglable;
if (lodash.has(this.props.node, 'children') && this.props.node.children.length > 0) {
childNodes = this.props.node.children.map(getTreeNode, this);
titleSty.color = this.props.node.selected ? '#7BB53B' : '#AF90A5';
} else {
titleSty.color = this.props.node.selected ? '#b58900' : '#afac87';
}
var isClosed = true;
if (lodash.has(this.props.node, 'closed')) isClosed = this.props.node.closed;
ulStyle.display = isClosed ? 'none' : 'inline-block';
var props = this.props;
var iconType = lodash.get(props, options.typeName);
if (iconType == options.icon.sun) iconSty.background = "url('./img/sun.ico') 0/16px no-repeat !important";
else if (iconType == options.icon.leaf) iconSty.background = "url('./img/leaf.ico') 0/16px no-repeat !important";
else if (iconType == options.icon.snow) iconSty.background = "url('./img/snow.ico') 0/16px no-repeat !important";
else iconSty.background = "url('./img/sun.ico') 0/16px no-repeat !important";
return (
<div id='TreeNode'>
<div id='pSty' style={pSty} className='FlexBox'>
<div id='iconSty' onClick={this.iconHandler} style={iconSty}> </div>
<div id='titleSty' onClick={this.clickHandler} style={titleSty} >{this.props.node.title}</div>
</div>
<ul id='ulStyle' style={ulStyle}>
{childNodes}
</ul>
</div>
);
}
}
class JTreeViewNode extends JTreeViewNodeRender {
constructor() {
super();
this.binder('clickHandler', 'iconHandler');
}
iconHandler() {
if (lodash.has(this.props.node, 'children') && this.props.node.children.length > 0) {
this.props.iconClick(this.props.node);
} else {
this.clickHandler();
}
}
clickHandler() { this.props.titleClick(this.props.node); }
}
class JTreeViewRender extends Component {
render() {
options = this.props.options;
var childNodes = this.props.data.map(getTreeNode, this);
return (
<div id='TreeRootSty' style={TreeRootSty}>
<ul id='ulSty' style={ulSty}>
{childNodes}
</ul>
</div>
);
}
}
export default class JTreeView extends JTreeViewRender {}
Upvotes: 0
Reputation: 8686
First, you have a circular dependency in your code, both Parent
and Child
require each other.
In order to avoid infinite loop when requiring modules, CommonJS modules act like this :
Parent.js
requires Child.js
, when Child
component calls require('Parent')
, the exported value of Parent.js
is an empty object. So you get the error e.toUpperCase is not a function
since e
(Parent
) is an empty object.
You should require after the module.exports
statement :
var Parent = React.createClass({
render: function() {
return (
<div>
<Child key={someKey} data={someData} />
</div>
);
},
});
module.exports = Parent;
var Child = require('Child');
But event if this solves the circular dependency, I don't understand what you're trying to achieve, it's an infinite loop actually.
Upvotes: 2