我正在尝试将带有孩子的变量传递给React中的父组件细微差别。我已经很接近要获得所需的过滤了,但是有两个小问题:
1。)筛选器函数似乎以“惰性”方式被调用。就像这样,当我在下拉字段中键入内容时,它只会通过更改之前在该字段中的内容。
2。)似乎我得到了匹配,但是下拉菜单div并没有增加任何内容,就像我希望的那样清除了。
以下是带有搜索下拉过滤器的模式:
class AssignModal extends React.Component {
constructor(props) {
super(props);
this.state = { usrSearch: '' };
this.handleTextChange = this.handleTextChange.bind(this);
this.handleFilter = this.handleFilter.bind(this);
}
handleTextChange(e) {
this.setState({ usrSearch: e.target.value });
this.handleFilter();
}
handleFilter(e) {
const input = this.state.usrSearch.trim();
if (!input) {
return;
}
this.props.filterDrop({ input: input });
}
render() {
return (
<div class="modal fade" id="assignModal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 style={{ color: 'red' }}><span class="glyphicon glyphicon-lock"></span>Assign</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<form role="form">
<div class="form-group">
<label for="usrname"><span class="glyphicon glyphicon-user"></span>Assign to:</label>
<div class="dropdown">
<input type="text" placeholder="Search.." id="myInput" value={this.state.usrSearch} onChange={this.handleTextChange} />
<div id="userDropdown" class="dropdown-content">
</div>
</div>
<br />
<button type="submit" class="btn btn-default btn-success btn-block"><span class="glyphicon glyphicon-off"></span>Assign</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-default btn-default pull-left" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>Cancel</button>
</div>
</div>
</div>
</div>
);
}
}
这是应该传递给该值的JavaScript函数(它确实传递了,我只是没有像我想要的那样添加/清除HTML元素):
filterUsers(input) {
var users = this.usersCollection.data;
var userDrop = $("#userDropdown");
var inputCheck = String(Object.values(input));
var userNames = users.map(function (e) { return e.userName });
var pos = userNames.indexOf(inputCheck);
alert(userNames);
for (var i = 0; i < userNames.length; i++) {
if (userNames[i].indexOf(inputCheck) > -1) {
userDrop.add("<p>Existing</p>");
alert("Found user");
}
else {
userDrop.empty();
alert("Didn't find user: " + inputCheck);
}
}
}
如果这完全不是我应该做的方式,请告诉我更好的方法。想法是在文本搜索框中从下拉菜单中填充已与要选择的过滤条件匹配的用户。
建议后更新代码:
票务组件
class Ticket extends React.Component {
constructor(props) {
super(props);
this.state = { data: this.props.initialData };
this.userState = { data: this.props.userData };
this.usersCollection = { data: this.props.userDB };
this.matchedUserNames = [];
this.serverMsg = { data: this.props.serverMsg };
this.serverMsgRst = { data: this.props.srvMsgRst };
this.getUsername = this.getUsername.bind(this);
this.isClosed = this.isClosed.bind(this);
this.toJavaScriptDate = this.toJavaScriptDate.bind(this);
this.handleLogSubmit = this.handleLogSubmit.bind(this);
this.checkServerMessage = this.checkServerMessage.bind(this);
this.showAssignModal = this.showAssignModal.bind(this);
this.filterUsers = this.filterUsers.bind(this);
}
filterUsers(input) {
var users = this.usersCollection.data;
var inputCheck = String(Object.values(input));
var userNames = users.map(function (e) { return e.userName });
const matchedUserNames = []
for (var i = 0; i < userNames.length; i++) {
if (userNames[i].indexOf(inputCheck) > -1) {
matchedUserNames.push(userNames[i])
}
}
//alert(matchedUserNames);
this.setState({ matchedUserNames });
}
render() {
var centerStyle = { alignItems: 'center', };
var closed = this.isClosed();
var closeTime = this.state.data.closeTime;
if (!this.state.data.apptConfirmItems || this.state.data.apptConfirmItems == 0) {
return (
<div class="queue">
<AssignModal filterDrop={this.filterUsers} matchedUserNames={this.matchedUserNames} />
<div style={{ textAlign: 'left' }}><p>Current Owner: {this.getUsername({ checkLog: this.state.data.userOwnerId })}<button style={{ marginLeft: '1%', display: 'inline' }} className="btn btn-info" id="assignTicket" onClick={this.showAssignModal}>Assign</button></p></div>
{closed}
<table className="table">
<tbody style={{ textAlign: 'center' }}>
<tr>
<td>
<h1>Affirm Logs</h1>
</td>
</tr>
<tr>
<td>
<h2>Summary: {this.state.data.summary}</h2>
</td>
</tr>
<tr>
<td>
<h5>Description: <i>{this.state.data.description}</i></h5>
</td>
</tr>
</tbody>
</table>
<div><h3>No logs at this time!</h3></div>
<LogForm onLogSubmit={this.handleLogSubmit} />
</div>
);
}
else {
return (
<div className="queue">
<AssignModal filterDrop={this.filterUsers} matchedUserNames={this.matchedUserNames} />
<div style={{ textAlign: 'left' }}><p>Current Owner: {this.getUsername({ checkLog: this.state.data.userOwnerId })}<button style={{ marginLeft: '1%', display: 'inline' }} className="btn btn-info" id="assignTicket" onClick={this.showAssignModal}>Assign</button></p></div>
{closed}
<table className = "table">
<tbody style={{ textAlign: 'center' }}>
<tr>
<td>
<h1>Affirm Logs</h1>
</td>
</tr>
<tr>
<td>
<h2>Summary: {this.state.data.summary}</h2>
</td>
</tr>
<tr>
<td>
<h5>Description: <i>{this.state.data.description}</i></h5>
</td>
</tr>
</tbody>
</table>
<LogList data={this.state.data.apptConfirmItems} checkUser={this.getUsername} renderDate={this.toJavaScriptDate} />
<LogForm onLogSubmit={this.handleLogSubmit} />
</div>
);
}
}
}
AssignModal组件
class AssignModal extends React.Component {
constructor(props) {
super(props);
this.state = { usrSearch: '' };
this.handleTextChange = this.handleTextChange.bind(this);
this.handleFilter = this.handleFilter.bind(this);
}
handleTextChange(e) {
this.setState({ usrSearch: e.target.value }, this.handleFilter);
}
handleFilter() {
const input = this.state.usrSearch.trim();
if (!input) {
return;
}
this.props.filterDrop({ input: input });
}
render() {
return (
<div class="modal fade" id="assignModal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 style={{ color: 'red' }}><span class="glyphicon glyphicon-lock"></span>Assign</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<form role="form">
<div class="form-group">
<label for="usrname"><span class="glyphicon glyphicon-user"></span>Assign to:</label>
<div class="dropdown">
<input type="text" placeholder="Search.." id="myInput" value={this.state.usrSearch} onChange={this.handleTextChange} />
<div id="userDropdown" class="dropdown-content">
{this.props.matchedUserNames.map(userName => <p>Existing: {userName}</p>)}
</div>
</div>
<br />
<button type="submit" class="btn btn-default btn-success btn-block"><span class="glyphicon glyphicon-off"></span>Assign</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-default btn-default pull-left" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>Cancel</button>
</div>
</div>
</div>
</div>
);
}
}
答案 0 :(得分:1)
- 过滤器函数似乎以“惰性”方式被调用。就像这样,当我在下拉字段中键入内容时,它只会通过更改之前该字段中的内容。
之所以要成为父母总是总是获得 previous 值,是因为在反应的意义上,您调用handleFilter()
的方式有点“早”。查看代码注释:
handleTextChange(e) {
// `setState` is async, you update `usrSearch` here
this.setState({ usrSearch: e.target.value });
this.handleFilter();
}
handleFilter(e) { // <-- off topic, but `e` is not used, remove it.
// but here, because it's async,
// `this.state.usrSearch` is not up-to-date yet
const input = this.state.usrSearch.trim();
if (!input) {
return;
}
this.props.filterDrop({ input: input });
}
// --- right way to call `handleFilter` ---
handleTextChange(e) {
// `setState` can accept a callback function as 2nd argument
// this ensures `handleFilter` is called only after `userSearch` is updated
this.setState({ usrSearch: e.target.value }, this.handleFilter);
}
- 似乎我能找到比赛,但是下拉列表div并没有增加任何东西,就像我希望的那样清除了。
现在在filterUsers()
中,您选择使用jQuery进行更新。由于上述异步问题,将发生以下情况:
setState()
异步调度对usrSearch
的更新,尚未发生handleFilter() -> filterUsers()
同步更新#userDropdown
的DOM(是的,确实发生了)setState()
发生了,所以usrSearch
得到了更新,AssignModal
重新呈现了我并不是说将jQuery与React混合总是错误的,但是在大多数情况下,如果您对React的理解不够的话。
如果这完全不是我应该做的方式,请告诉我更好的方法。
如果使用this.setState({ usrSearch: e.target.value }, this.handleFilter)
,则由于异步问题已解决,因此第二个问题应自动消失。但这不是最佳的选择。
这是一个更好的方法:
// 1. instead of use jQuery to update DOM right away, you update "state"
filterUsers(input) {
var users = this.usersCollection.data;
var inputCheck = String(Object.values(input));
var userNames = users.map(function (e) { return e.userName });
var pos = userNames.indexOf(inputCheck);
const matchedUserNames = []
for (var i = 0; i < userNames.length; i++) {
if (userNames[i].indexOf(inputCheck) > -1) {
matchedUserNames.push(userNames[i])
}
}
this.setState({ matchedUserNames })
}
// 2. then you pass the `matchedUserNames` to child `AssignModal`
<AssignModal matchedUserNames={this.state.matchedUserNames} /*...*/ />
// 3. in AssignModel's render, do the DOM update using React
<div id="userDropdown" class="dropdown-content">
{this.props.matchedUserNames.map(userName => <p>Existing: {userName}</p>)}
</div>
此外,将usrSearch
保持为AssignModel
的状态也不是一个好主意。由于您的父母也像filterUsers(input)
一样读取此值,因此最好保持父母的状态,然后将其作为道具传递给AssignModel
。我将把这部分留给你。
答案 1 :(得分:0)
@hackape我终于不得不接受您的回答。我遇到了一个不同的问题,它试图用setState更新一个单独的JavaScript var,而不是将其实际设置为state。但是您在其他问题之外的建议非常有帮助-非常感谢!