使反应组件不那么冗长
Make react components less verbose
我会在问题的最后展示完整的代码。但我想关注的是我的 App.jsx 反应组件
中的渲染函数
render() {
return (
<div className="container">
<ul>
{this.renderListApplications()}
</ul>
<div>{JSON.stringify(this.data.currentApplication)}</div>
<form className="new-task" onSubmit={this.handleSubmit} >
<input ref="input_36" id="input_36" type="text" value={this.state.input_36} onChange={this.handleChange} />
<input ref="input_37" id="input_37" type="text" value={this.state.input_37} onChange={this.handleChange} />
<textarea ref="input_38" id="input_38" onChange={this.handleChange} value={this.state.input_38} />
<textarea ref="input_39" id="input_39" onChange={this.handleChange} value={this.state.input_39} />
<input ref="input_40" id="input_40" type="text" value={this.state.input_40} onChange={this.handleChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
我发现 ref="input_36" id="input_36" type="text" value={this.state.input_36} onChange={this.handleChange}
太冗长了。如果我有大约 300 个其他输入、textarea、select 字段散布在一大堆其他 html 元素之间,我会尽可能减少打字量。是否有更方便的方法以编程方式设置输入、文本区域和 select 字段的某些属性和事件处理程序?或者某个地方的反应设置默认这些东西中的一些?或者只是一些让事情不那么冗长的方法?
如果这是 PHP,我会将所有 html 代码放入 form.html
,然后我会以编程方式设置所有这些属性,例如:
/* I could simplify this code even more, but I'm making it verbose so you understand what I'm trying to achieve. */
$htmlForm = new DOMDocument();
$htmlForm->loadHTMLFile('form.html');
foreach ($htmlForm->getElementsByTagName('input') as $i)
{
$i->setAttribute('value',$arrState[$i->getAttribute('name')]);
$i->setAttribute('onChange','javascript:somefunction()');
etc...
}
foreach ($htmlForm->getElementsByTagName('textarea') as $i)
{
$i->nodeValue = $arrState[$i->getAttribute('name')];
$i->setAttribute('onChange','javascript:somefunction()');
etc...
}
当我尝试各种方法以编程方式在 React 和 JS 中设置这些属性时,我一直 运行 陷入关于任何 DOM 或元素或任何不存在的错误,否则我的应用程序会崩溃,管他呢。如果有人可以向我展示如何减少冗长的示例,那就太好了。
现在这是我的完整 App.jsx 文件。
App = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
return {
applications: Applications.find({}, {sort: {createdAt: -1}}).fetch(),
currentApplication: Applications.findOne({_id:this.props.router.params.appid}, {sort: {createdAt: -1}}),
}
},
getInitialState: function() {
return this.loadForm(this.props.router.params.appid);
},
loadForm(appId) {
var currentApp = Applications.findOne({_id:appId});
if(!currentApp) currentApp = {};
return currentApp;
},
clickLoadForm(appId)
{
var currentApp = this.loadForm(appId);
var state = new Object();
var refs = this.refs;
Object.keys(refs).map(function(prop,index){
state[prop] = typeof currentApp[prop] == 'undefined' ? "" : currentApp[prop];
});
this.setState(state);
},
renderListApplications() {
var _this = this;
return this.data.applications.map(function(applicationform,i) {
return <li key={"li"+i}><a onClick={_this.clickLoadForm.bind(_this,applicationform._id)} href={Meteor.absoluteUrl()+'application/' +applicationform._id} key={"a"+i}>Version {applicationform._id}</a></li>;
});
},
handleSubmit(event) {
event.preventDefault();
var refs = this.refs;
var formVals = new Object();
Object.keys(refs).map(function(prop, index){
if(refs[prop].nodeName.match(/(INPUT|SELECT|TEXTAREA)/).length > 0)
formVals[prop] = refs[prop].value;
});
Meteor.call("saveApplication", formVals);
},
handleChange: function(e) {
if(!e.target.id) return;
if(typeof e.target.id == 'undefined') return;
var state = new Object();
state[e.target.id] = e.target.value;
this.setState(state);
},
render() {
return (
<div className="container">
<ul>
{this.renderListApplications()}
</ul>
<div>{JSON.stringify(this.data.currentApplication)}</div>
<form className="new-task" onSubmit={this.handleSubmit} >
<input ref="input_36" id="input_36" type="text" value={this.state.input_36} onChange={this.handleChange} />
<input ref="input_37" id="input_37" type="text" value={this.state.input_37} onChange={this.handleChange} />
<textarea ref="input_38" id="input_38" onChange={this.handleChange} value={this.state.input_38} />
<textarea ref="input_39" id="input_39" onChange={this.handleChange} value={this.state.input_39} />
<input ref="input_40" id="input_40" type="text" value={this.state.input_40} onChange={this.handleChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
});
备注
- 即使有值,我也希望能够编辑表单域
在里面
- 当有人点击
renderListApplications
中的 link 时,我
希望表单重新呈现与对应的相关值
url 中的 :appid
您可以简单地在组件中创建函数来构建 UI 的一部分(如工厂)。这是一个例子:
getInputField(id){
const inputId = `input_${id}`;
return(
<input ref={inputId}
id={inputId}
type="text"
value={this.state[`input${id}`]}
onChange={this.handleChange} />
);
}
render(){
const input36 = this.getInputField(36);
const input37 = this.getInputField(37);
return (
<div className="container">
<ul>
{this.renderListApplications()}
</ul>
<div>{JSON.stringify(this.data.currentApplication)}</div>
<form className="new-task" onSubmit={this.handleSubmit} >
{input36}
{input37}
...
</form>
</div>
);
}
如果有 300 个字段,kadrian 的答案不能解决问题。您仍然有 300 行无法在其他地方构建。我建议 map
:
render() {
const inputs = [
[ 12, 'text', this.handle1 ],
[ 13, 'password', this.handle1 ],
[ 14, 'textarea', this.handle1 ]
];
return <div>
<form> {
inputs.map( ( input ) => <ReactComponents.input
key={ index }
ref={ input[ 0 ] }
data={ input }
/>; )
} </form>
</div>;
以及管理所有用例的组件:
ReactComponents.input = ...
...
render() {
...
if( this.props.data[ 1 ] === 'textarea' )
return <textarea
...
/>
return <input
type={ this.props.data[ 1 ] }
ref={ this.props.data[ 0 ] }
key={ this.props.data[ 0 ] }
onChange={ this.props.data[ 2 ] }
...
/>
}
因此您可以在其他地方(在 x-config 文件中?)构建您的 inputs
数组,然后将其传递到您的视图中以构建所有内容。
我会在问题的最后展示完整的代码。但我想关注的是我的 App.jsx 反应组件
中的渲染函数render() {
return (
<div className="container">
<ul>
{this.renderListApplications()}
</ul>
<div>{JSON.stringify(this.data.currentApplication)}</div>
<form className="new-task" onSubmit={this.handleSubmit} >
<input ref="input_36" id="input_36" type="text" value={this.state.input_36} onChange={this.handleChange} />
<input ref="input_37" id="input_37" type="text" value={this.state.input_37} onChange={this.handleChange} />
<textarea ref="input_38" id="input_38" onChange={this.handleChange} value={this.state.input_38} />
<textarea ref="input_39" id="input_39" onChange={this.handleChange} value={this.state.input_39} />
<input ref="input_40" id="input_40" type="text" value={this.state.input_40} onChange={this.handleChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
我发现 ref="input_36" id="input_36" type="text" value={this.state.input_36} onChange={this.handleChange}
太冗长了。如果我有大约 300 个其他输入、textarea、select 字段散布在一大堆其他 html 元素之间,我会尽可能减少打字量。是否有更方便的方法以编程方式设置输入、文本区域和 select 字段的某些属性和事件处理程序?或者某个地方的反应设置默认这些东西中的一些?或者只是一些让事情不那么冗长的方法?
如果这是 PHP,我会将所有 html 代码放入 form.html
,然后我会以编程方式设置所有这些属性,例如:
/* I could simplify this code even more, but I'm making it verbose so you understand what I'm trying to achieve. */
$htmlForm = new DOMDocument();
$htmlForm->loadHTMLFile('form.html');
foreach ($htmlForm->getElementsByTagName('input') as $i)
{
$i->setAttribute('value',$arrState[$i->getAttribute('name')]);
$i->setAttribute('onChange','javascript:somefunction()');
etc...
}
foreach ($htmlForm->getElementsByTagName('textarea') as $i)
{
$i->nodeValue = $arrState[$i->getAttribute('name')];
$i->setAttribute('onChange','javascript:somefunction()');
etc...
}
当我尝试各种方法以编程方式在 React 和 JS 中设置这些属性时,我一直 运行 陷入关于任何 DOM 或元素或任何不存在的错误,否则我的应用程序会崩溃,管他呢。如果有人可以向我展示如何减少冗长的示例,那就太好了。
现在这是我的完整 App.jsx 文件。
App = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
return {
applications: Applications.find({}, {sort: {createdAt: -1}}).fetch(),
currentApplication: Applications.findOne({_id:this.props.router.params.appid}, {sort: {createdAt: -1}}),
}
},
getInitialState: function() {
return this.loadForm(this.props.router.params.appid);
},
loadForm(appId) {
var currentApp = Applications.findOne({_id:appId});
if(!currentApp) currentApp = {};
return currentApp;
},
clickLoadForm(appId)
{
var currentApp = this.loadForm(appId);
var state = new Object();
var refs = this.refs;
Object.keys(refs).map(function(prop,index){
state[prop] = typeof currentApp[prop] == 'undefined' ? "" : currentApp[prop];
});
this.setState(state);
},
renderListApplications() {
var _this = this;
return this.data.applications.map(function(applicationform,i) {
return <li key={"li"+i}><a onClick={_this.clickLoadForm.bind(_this,applicationform._id)} href={Meteor.absoluteUrl()+'application/' +applicationform._id} key={"a"+i}>Version {applicationform._id}</a></li>;
});
},
handleSubmit(event) {
event.preventDefault();
var refs = this.refs;
var formVals = new Object();
Object.keys(refs).map(function(prop, index){
if(refs[prop].nodeName.match(/(INPUT|SELECT|TEXTAREA)/).length > 0)
formVals[prop] = refs[prop].value;
});
Meteor.call("saveApplication", formVals);
},
handleChange: function(e) {
if(!e.target.id) return;
if(typeof e.target.id == 'undefined') return;
var state = new Object();
state[e.target.id] = e.target.value;
this.setState(state);
},
render() {
return (
<div className="container">
<ul>
{this.renderListApplications()}
</ul>
<div>{JSON.stringify(this.data.currentApplication)}</div>
<form className="new-task" onSubmit={this.handleSubmit} >
<input ref="input_36" id="input_36" type="text" value={this.state.input_36} onChange={this.handleChange} />
<input ref="input_37" id="input_37" type="text" value={this.state.input_37} onChange={this.handleChange} />
<textarea ref="input_38" id="input_38" onChange={this.handleChange} value={this.state.input_38} />
<textarea ref="input_39" id="input_39" onChange={this.handleChange} value={this.state.input_39} />
<input ref="input_40" id="input_40" type="text" value={this.state.input_40} onChange={this.handleChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
});
备注
- 即使有值,我也希望能够编辑表单域 在里面
- 当有人点击
renderListApplications
中的 link 时,我 希望表单重新呈现与对应的相关值 url 中的
:appid
您可以简单地在组件中创建函数来构建 UI 的一部分(如工厂)。这是一个例子:
getInputField(id){
const inputId = `input_${id}`;
return(
<input ref={inputId}
id={inputId}
type="text"
value={this.state[`input${id}`]}
onChange={this.handleChange} />
);
}
render(){
const input36 = this.getInputField(36);
const input37 = this.getInputField(37);
return (
<div className="container">
<ul>
{this.renderListApplications()}
</ul>
<div>{JSON.stringify(this.data.currentApplication)}</div>
<form className="new-task" onSubmit={this.handleSubmit} >
{input36}
{input37}
...
</form>
</div>
);
}
如果有 300 个字段,kadrian 的答案不能解决问题。您仍然有 300 行无法在其他地方构建。我建议 map
:
render() {
const inputs = [
[ 12, 'text', this.handle1 ],
[ 13, 'password', this.handle1 ],
[ 14, 'textarea', this.handle1 ]
];
return <div>
<form> {
inputs.map( ( input ) => <ReactComponents.input
key={ index }
ref={ input[ 0 ] }
data={ input }
/>; )
} </form>
</div>;
以及管理所有用例的组件:
ReactComponents.input = ...
...
render() {
...
if( this.props.data[ 1 ] === 'textarea' )
return <textarea
...
/>
return <input
type={ this.props.data[ 1 ] }
ref={ this.props.data[ 0 ] }
key={ this.props.data[ 0 ] }
onChange={ this.props.data[ 2 ] }
...
/>
}
因此您可以在其他地方(在 x-config 文件中?)构建您的 inputs
数组,然后将其传递到您的视图中以构建所有内容。