使用 JSX 时 ReactDOM 渲染失败
ReactDOM render fails when using JSX
我正在使用 ReactDOM.render()
渲染我创建的组件。由于实现的细节,该组件相当复杂,但我可以轻松呈现它 iff 我避免使用 JSX 语法。但是,如果我使用 JSX,我根本无法渲染组件,并且出现以下错误:
TypeError: _this2.props.children.forEach is not a function
我的代码可以在下面看到(我也收到了一些警告,我还没有开始修复,所以你可以暂时忽略它们)。请记住,组件的 HTML 结构非常严格(由于我使用的 CSS 框架)并且无法更改。有没有一种方法可以使用 JSX 来实现相同的结果,如果可以,我做错了什么?
// This function was based on this answer:
function generateUniqueId() {
// always start with a letter (for DOM friendlyness)
var idstr = String.fromCharCode(Math.floor((Math.random() * 25) + 65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode = Math.floor((Math.random() * 42) + 48);
if (ascicode < 58 || ascicode > 64) {
// exclude all chars between : (58) and @ (64)
idstr += String.fromCharCode(ascicode);
}
} while (idstr.length < 32);
return (idstr);
}
// Technically this is not exactly a component, but I use it as such to make things simpler.
class Tab extends React.Component {
render() {
return React.createElement('div', {}, this.props.children);
}
}
// This is my Tabs component
class Tabs extends React.Component {
// In the constructor, I take all children passed to the component
// and push them to state with the necessary changes made to them.
constructor(props) {
super(props);
var state = {
group: 'tab_group_' + generateUniqueId(),
children: []
}
this.props.children.forEach(
function(child) {
if (!child instanceof Tab) {
throw "All children of a 'Tabs' component need to be of type 'Tab'. Expected type: 'Tab' Found Type: '" + child.class + "'";
return;
}
var tab = Object.assign({}, child);
tab.internalId = 'tab_' + generateUniqueId();
state.children.push(tab);
}
);
this.state = state;
}
// When rendering, I don't render the children as needed, but I create
// the structure I need to use for the final result.
render() {
var childrenToRender = [];
var groupName = this.state.group;
this.state.children.forEach(function(tab) {
childrenToRender.push(
React.createElement(
'input', {
type: 'radio',
name: groupName,
id: tab.internalId,
checked: true,
'aria-hidden': 'true'
}
)
);
childrenToRender.push(
React.createElement(
'label', {
'htmlFor': tab.internalId,
'aria-hidden': 'true'
},
'demo-tab'
)
);
childrenToRender.push(React.createElement('div', {}, tab.props.children));
});
return React.createElement('div', {
'className': 'tabs'
}, childrenToRender);
}
}
// This works fine
ReactDOM.render(
React.createElement(Tabs, {}, [React.createElement(Tab, {}, 'Hello world')]),
document.getElementById('root')
);
// This fails with the error mentioned above
// ReactDOM.render(
// <Tabs>
// <Tab>Hello, world!</Tab>
// </Tabs>,
// document.getElementById('root')
// );
<link rel="stylesheet" href="https://gitcdn.link/repo/Chalarangelo/mini.css/master/dist/mini-default.min.css">
<script src="https://unpkg.com/react@latest/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<div id="root"></div>
更新: 由于处理方式的原因,只有当我实际上只将一个 <Tab>
传递给 <Tabs>
时才会发生这种情况。例如,如果我使用以下代码,我可以使用 JSX 来渲染组件及其内容:
ReactDOM.render(
<Tabs>
<Tab>Hello, world!</Tab>
<Tab>Hello, world!</Tab>
</Tabs>,
document.getElementById('root')
);
在检查了 JSX 代码的 babel
输出后,我意识到它没有输出类似 [React.createElement(Tab, {}, 'Hello world')]
的东西,而是更像 React.createElement(Tab, {}, 'Hello world')
的东西,这意味着它不是一个数组,从而导致 .forEach()
.
出现问题
对于任何感兴趣的人,我所做的是检查 this.props.children
是否是一个数组,如果不是,则将其实际转换为一个数组。示例如下:
if (!Array.isArray(this.props.children))
var tempProps = [this.props.children];
else
var tempProps = this.props.children;
tempProps.forEach(
// Rest of the code is pretty much the same as before
);
这不是一个非常优雅的解决方案,所以如果您知道任何更优雅的答案,请随时post。
我正在使用 ReactDOM.render()
渲染我创建的组件。由于实现的细节,该组件相当复杂,但我可以轻松呈现它 iff 我避免使用 JSX 语法。但是,如果我使用 JSX,我根本无法渲染组件,并且出现以下错误:
TypeError: _this2.props.children.forEach is not a function
我的代码可以在下面看到(我也收到了一些警告,我还没有开始修复,所以你可以暂时忽略它们)。请记住,组件的 HTML 结构非常严格(由于我使用的 CSS 框架)并且无法更改。有没有一种方法可以使用 JSX 来实现相同的结果,如果可以,我做错了什么?
// This function was based on this answer:
function generateUniqueId() {
// always start with a letter (for DOM friendlyness)
var idstr = String.fromCharCode(Math.floor((Math.random() * 25) + 65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode = Math.floor((Math.random() * 42) + 48);
if (ascicode < 58 || ascicode > 64) {
// exclude all chars between : (58) and @ (64)
idstr += String.fromCharCode(ascicode);
}
} while (idstr.length < 32);
return (idstr);
}
// Technically this is not exactly a component, but I use it as such to make things simpler.
class Tab extends React.Component {
render() {
return React.createElement('div', {}, this.props.children);
}
}
// This is my Tabs component
class Tabs extends React.Component {
// In the constructor, I take all children passed to the component
// and push them to state with the necessary changes made to them.
constructor(props) {
super(props);
var state = {
group: 'tab_group_' + generateUniqueId(),
children: []
}
this.props.children.forEach(
function(child) {
if (!child instanceof Tab) {
throw "All children of a 'Tabs' component need to be of type 'Tab'. Expected type: 'Tab' Found Type: '" + child.class + "'";
return;
}
var tab = Object.assign({}, child);
tab.internalId = 'tab_' + generateUniqueId();
state.children.push(tab);
}
);
this.state = state;
}
// When rendering, I don't render the children as needed, but I create
// the structure I need to use for the final result.
render() {
var childrenToRender = [];
var groupName = this.state.group;
this.state.children.forEach(function(tab) {
childrenToRender.push(
React.createElement(
'input', {
type: 'radio',
name: groupName,
id: tab.internalId,
checked: true,
'aria-hidden': 'true'
}
)
);
childrenToRender.push(
React.createElement(
'label', {
'htmlFor': tab.internalId,
'aria-hidden': 'true'
},
'demo-tab'
)
);
childrenToRender.push(React.createElement('div', {}, tab.props.children));
});
return React.createElement('div', {
'className': 'tabs'
}, childrenToRender);
}
}
// This works fine
ReactDOM.render(
React.createElement(Tabs, {}, [React.createElement(Tab, {}, 'Hello world')]),
document.getElementById('root')
);
// This fails with the error mentioned above
// ReactDOM.render(
// <Tabs>
// <Tab>Hello, world!</Tab>
// </Tabs>,
// document.getElementById('root')
// );
<link rel="stylesheet" href="https://gitcdn.link/repo/Chalarangelo/mini.css/master/dist/mini-default.min.css">
<script src="https://unpkg.com/react@latest/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<div id="root"></div>
更新: 由于处理方式的原因,只有当我实际上只将一个 <Tab>
传递给 <Tabs>
时才会发生这种情况。例如,如果我使用以下代码,我可以使用 JSX 来渲染组件及其内容:
ReactDOM.render(
<Tabs>
<Tab>Hello, world!</Tab>
<Tab>Hello, world!</Tab>
</Tabs>,
document.getElementById('root')
);
在检查了 JSX 代码的 babel
输出后,我意识到它没有输出类似 [React.createElement(Tab, {}, 'Hello world')]
的东西,而是更像 React.createElement(Tab, {}, 'Hello world')
的东西,这意味着它不是一个数组,从而导致 .forEach()
.
对于任何感兴趣的人,我所做的是检查 this.props.children
是否是一个数组,如果不是,则将其实际转换为一个数组。示例如下:
if (!Array.isArray(this.props.children))
var tempProps = [this.props.children];
else
var tempProps = this.props.children;
tempProps.forEach(
// Rest of the code is pretty much the same as before
);
这不是一个非常优雅的解决方案,所以如果您知道任何更优雅的答案,请随时post。