React Js 组件在状态更改后只渲染一次
React Js component rendering only once after state change
我对 ReactJS 很陌生,我的问题很不寻常,可能只是因为我没有按照它们应该的方式实现事情。
所以基本上来说,这以前工作正常,但我需要添加一些新功能,而且......嗯,有些东西不对劲。
首先 - ConvFrame
是顶部组件,显示在页面顶部,它由ConvForm
组件(用于添加新查询)和未分配和新调用所在的ConvList
组成。此处ConvList
的 ID 和密钥为 1。
下面还有工作人员列表,他们仅使用ConvForm
,字段本身是放置区,可以快速分配新的呼叫任务。此处ConvList
的 Id 和键等于辅助角色的 ID。
呈现ConvList
时,它会向服务器查询列表中的作业。这对他们所有人来说都很好。但是,当通过ConvForm
添加新项目时,似乎存在一些奇怪的问题。它调用handleCommentSubmit()
函数,调用this.loadCommentsFromServer();
(愚蠢,我知道!),然后为记录设置新状态this.setState({records: data});
添加第一条记录时,/api/zlecenia
被调用两次。一次来自ConvFrame
内部的loadCommentsFromServer()
,第二次来自ConvList
内部。通过表单添加第二条记录调用它一次,组件似乎对状态更改没有反应。有些东西实施得很糟糕,我猜。
以下是源代码:对话.js
//For dragging
var placeholder = document.createElement("div");
placeholder.className = "placeholder";
var dragged;
var over;
/**
* Conversation
* Should be used for listing conversation blocks, adds class based on age of task.
* Detects drag events, renders block, calls dragEnd function to append block to new
* location and uses props.OnDrop function to pass id of element and target id of worker
*/
window.Conversation = React.createClass({
dynamicClass: function () {
return "convo " + this.props.delay;
},
dragStart: function (e) {
dragged = e.currentTarget;
over = null;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData("text/html", e.currentTarget);
},
dragEnd: function (e) {
$(dragged).show();
$(placeholder).remove();
console.log(over, over.className);
if (over && over.className === "candrop") {
var id = Number(dragged.dataset.id);
this.props.onDrop({id: id, target: over.id});
over.appendChild(dragged);
}else{
console.log('returning base:' + over);
$(dragged).parent().append(dragged);
}
},
render: function() {
return (
<div draggable="true" data-id={this.props.id} onDragEnd={this.dragEnd} onDragStart={this.dragStart} className={this.dynamicClass()} >
{this.props.children}
</div>
);
}
});
/**
* ConvList
* Displays conversation dropdown list. I should aim to make it single component, do not repeat for workers.
* Detects dragOver for .candrop and place some funny placeholder. Detect position change from Conversation view
* call master class from parent component and pass data. Detect delete event.
*/
window.ConvList = React.createClass({
getInitialState: function () {
return {data: []};
},
loadConvsFromServer: function () {
$.ajax({
url: baseUrl + '/api/zlecenia',
type: 'GET',
data: {id: this.props.id},
success: function (data) {
this.setState({data: data});
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function () {
this.loadConvsFromServer();
},
dragOver: function (e) {
e.preventDefault();
$(dragged).fadeOut();
if (e.target.className === "candrop") {
if (e.target.className == "placeholder")
return;
over = e.target;
e.target.appendChild(placeholder, e.target);
}
},
updatePosition: function (data) {
console.log('update convo %d for member %e', data.id, data.target);
$.ajax({
url: baseUrl + '/api/zlecenia',
type: 'PUT',
data: {id: data.id, assign: data.target},
success: function (data) {
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
deleteTask: function (e) {
var taskIndex = parseInt(e.target.value, 10);
console.log('remove task: %d', taskIndex);
$.ajax({
url: baseUrl + '/api/zlecenia/' + taskIndex,
type: 'DELETE',
success: function (data) {
this.loadConvsFromServer();
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function () {
return (
<div className="convlist" onDragOver={this.dragOver}>
<div className="candrop" id={this.props.id} >{this.props.id}
{this.state.data.map((c) =>
<Conversation onDrop={this.updatePosition} delay={c.delayTime} id={c.id} key={c.id}>
<p className="convTop">{c.formattedTime} | Tel. {c.phone} | {c.name} | Nr.Rej {c.number}</p>
<p>{c.text}</p>
<button className="deleteConv" onClick={this.deleteTask} value={c.id}>x</button>
</Conversation>
)}
</div>
</div>
);
}
});
/**
* ConvForm
* Displays conversation create form. Prepares fields, validates them.
* Call master function to add new record on send.
*/
var ConvForm = React.createClass({
getInitialState: function () {
return {phone: '', name: '', number: '', text: ''};
},
handlePhoneChange: function (e) {
this.setState({phone: e.target.value});
},
handleNameChange: function (e) {
this.setState({name: e.target.value});
},
handleNumberChange: function (e) {
this.setState({number: e.target.value});
},
handleTextChange: function (e) {
this.setState({text: e.target.value});
},
submitForm: function (e) {
e.preventDefault();
var phone = this.state.phone.trim();
var name = this.state.name.trim();
var number = this.state.number.trim();
var text = this.state.text.trim();
if (!text || !phone || !name || !number) {
return;
}
this.props.onConvSubmit({phone: phone, name: name, number: number, text: text});
this.setState({phone: '', text: '', number: '', name: ''});
},
render: function () {
return (
<form className="convForm" onSubmit={this.submitForm}>
<div className="row">
<div className="col-xs-12 col-md-4">
<div className="form-group">
<input
className="form-control"
type="text"
placeholder="Telefon"
value={this.state.phone}
onChange={this.handlePhoneChange}
/>
</div>
</div>
<div className="col-xs-12 col-md-4">
<div className="form-group">
<input
className="form-control"
type="text"
placeholder="Imię i nazwisko"
value={this.state.name}
onChange={this.handleNameChange}
/>
</div>
</div>
<div className="col-xs-12 col-md-4">
<div className="form-group">
<input
className="form-control"
type="text"
placeholder="Nr. rejestracyjny"
value={this.state.number}
onChange={this.handleNumberChange}
/>
</div>
</div>
</div>
<div className="form-group">
<textarea
className="form-control"
type="text"
placeholder="Treść"
value={this.state.text}
onChange={this.handleTextChange}
/>
</div>
<input className="btn btn-success" type="submit" value="Zapisz" />
</form>
);
}
});
/**
* ConvFrame
* Conversation main frame and root functions for both form and conversations listing.
*/
window.ConvFrame = React.createClass({
getInitialState: function () {
return {records: []};
},
loadCommentsFromServer: function () {
$.ajax({
url: baseUrl + '/api/zlecenia',
type: 'GET',
data: {id : 1},
success: function (data) {
this.setState({records: data});
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function (convo) {
$.ajax({
url: baseUrl + '/api/zlecenia',
type: 'POST',
data: convo,
success: function (data) {
this.loadCommentsFromServer();
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="add-new">
<div className="row">
<div className="col-xs-12 col-md-12 frame">
<div className="col-xs-12 col-md-7">
<h3>Dodaj nową rozmowę</h3>
<ConvForm onConvSubmit={this.handleCommentSubmit} />
</div>
<div className="col-xs-12 col-md-5">
<ConvList key='1' id='1' data={this.state.records} />
</div>
</div>
</div>
</div>
);
}
});
工人.js
/**
* WorkerList
*
*/
var Worker = React.createClass({
render: function () {
return (
<div>
{this.props.children}
</div>
);
}
});
/**
* WorkerList
*
*/
var WorkerList = React.createClass({
render: function () {
return (
<div className="worker-list">
{this.props.data.map((worker) =>
<Worker id={worker.id} key={worker.id}>
<div className="row">
<div className="col-xs-12 col-md-12 frame">
<div className="col-xs-12 col-md-5">
<h4>{worker.username}</h4>
</div>
<div className="col-xs-12 col-md-7">
<ConvList key={worker.id} id={worker.id} />
</div>
</div>
</div>
</Worker>
)}
</div>
);
}
});
/**
* WorkerForm
*
*/
var WorkerForm = React.createClass({
render: function() {
return (
<div>
</div>
);
}
});
/**
* WorkerFame
*
*/
window.WorkerFrame = React.createClass({
getInitialState: function () {
return {data: []};
},
loadWorkersFromServer: function () {
$.ajax({
url: baseUrl + '/api/pracownicy',
type: 'GET',
success: function (data) {
this.setState({data: data});
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function () {
this.loadWorkersFromServer();
},
handleWorkerSubmit: function (worker) {
$.ajax({
url: baseUrl + '/api/pracownicy',
type: 'POST',
data: worker,
success: function (data) {
this.loadWorkersFromServer();
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="add-new">
<div className="row">
<div className="col-xs-12 col-md-12">
<WorkerList data={this.state.data} />
</div>
<div className="col-xs-12 col-md-12">
<WorkerForm onWorkerSubmit={this.handleWorkerSubmit} />
</div>
</div>
</div>
);
}
});
决赛.js
var DashFrame = React.createClass({
render: function() {
return (
<div>
<ConvFrame/>
<WorkerFrame/>
</div>
);
}
});
ReactDOM.render(
<DashFrame/>,
document.getElementById('dashboard')
);
任何提示将不胜感激。我的大脑在沸腾。
从(长)代码中,我认为您的问题出现了,因为您对 <ConvList>
中的服务器的调用在 componentDidMount()
中:
componentDidMount: function () {
this.loadConvsFromServer();
},
componentDidMount()
在初始挂载时始终只调用一次。
所以第二次,react 不调用componentDidMount()
,不调用服务器,你的状态不会改变,因此<ConvList>
不会更新。
要解决此问题,请添加:
componentDidUpdate: function () {
this.loadConvsFromServer();
},
更新组件时也称为。
更新:您还需要向生成的setState()
添加一个条件,否则您将获得一个无限循环(componentDidUpdate()
-> setState()
-> componentDidUpdate()
->重复)。
最好的地方可能是loadConvsFromServer()
里面。像这样:
...
success: function (data) {
var dataChanged = ...
// some smart comparison of data with this.state.data
if (dataChanged) {
this.setState({data: data});
}
}.bind(this),
...
另外,我在<ConvForm>
内部submitForm()
注意到了这个片段:
this.props.onConvSubmit({phone: phone, name: name, number: number, text: text});
this.setState({phone: '', text: '', number: '', name: ''});
这是一个危险的组合:第一次调用实质上是将控制权传回父级,父级可能会(并且可能会)重新渲染整个树。紧接着,您使用 setState()
触发第二次渲染。 但是没有办法知道它们将以哪个顺序被解雇。
更干净(并且更易于调试和维护)是将清除所有输入的setState()
移动到componentWillReceiveProps()
生命周期方法。这样,每次组件由其父组件重新渲染时,所有输入都将清除。奖励:您的组件只会重新渲染一次。
- Javascript返回值只在循环中返回一次
- Jquery FadeIn FadeOut 只工作一次
- Javascript html每点击一次就会更改url
- 如何在chrome扩展中存储数据/结果,以及如何使用setTimeout使其只被调用一次
- Rails操作只调用一次,但我在ajax中每秒钟都调用一次
- jQuery滚动功能只工作一次
- 刷新导致我的帖子“;张贴“;再一次
- 引导程序崩溃一次只能看到一个
- 有没有一个抽象层,这样我就可以集成一次,然后使用pusher、pubnub或faye
- 加载器组件仅加载一次
- 根据Angular.JS上一次的内容禁用选择
- 一次又一次地在新的和相同的选项卡中打开一个url
- 只在宽度以下和宽度以上各准备一次
- React Js 组件在状态更改后只渲染一次
- 一旦套接字发出事件(?),每5秒更新一次组件
- 在掉落一个组件时,它不止一次地添加到可掉落的组件上
- React组件在一次或多次点击中获取请求
- 每秒钟更新一次React组件
- 为什么我的React组件渲染调用两次,一次没有数据,然后有数据,但太晚的例外
- 为什么反应.即使我有多个目标元素,组件也只渲染一次