javascript - React - 在下拉列表中过滤?

标签 javascript reactjs

我试图在 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">&times;</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">&times;</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>
            );
        }
    }

最佳答案

  1. The filter function appears to be called in a "lazy" fashion. As in, when I type in the dropdown field, it'll ONLY pass what was PREVIOUSLY in the field before I changed it.

为什么你的父级总是获得前一个值,是因为你调用handleFilter()的方式在React的意义上有点“早期”。参见代码注释:

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);
}
<小时/>
  1. It seems that I get matches, but nothing adds to the dropdown div and clear it like I would hope.

现在在 filterUsers() 中,您选择使用 jQuery 进行更新。由于上述异步问题,将会发生以下情况:

  1. setState() 异步安排对 usrSearch 的更新,但尚未发生
  2. handleFilter() -> filterUsers() 同步更新 #userDropdown 的 DOM(是的,它确实发生了)
  3. 预定的 setState() 现在发生,因此 usrSearch 得到更新,并且 AssignModal 重新渲染
  4. 重新渲染只是将您的 jQuery DOM 更新覆盖回空

我并不是说将 jQuery 与 React 混合在一起总是错误的,但大多数时候都是错误的,如果你对 React 不够了解的话。

<小时/>

If this is totally not the way I should be doing this, let me know a better way.

如果您使用 this.setState({ usrSearch: e.target.value }, this.handleFilter),那么由于异步问题已修复,问题 2 应该会自动消失。但这不是最佳方式。

这里有一个更好的方法:

// 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) 中那样读取此值,因此您最好保留父级的状态,然后将其作为 prop 传递给 AssignModel。我将把这部分留给你。

关于javascript - React - 在下拉列表中过滤?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55681923/

相关文章:

javascript - 当 onfocus 设置选择大小时,Google Chrome 会崩溃

javascript - 在 Enter 上触发 javascript 函数

reactjs - Setstate 的三元运算符

javascript - 如何从 Rails View 调用内部 API(用于 ReactJS 预渲染目的)?

reactjs - 如何编辑react bootstrap表并在编辑后保存数据

javascript - 如何确定 react 中折叠元素的状态

javascript - 如何共享 Swagger 文档

javascript - 从 JavaScript 中的类实例动态继承

javascript - 导入react的方式有什么问题

javascript - 什么是客户端 MVC,它是如何在 JavaScript 中实现的?