【问题标题】:React - Filter In Dropdown?React - 在下拉列表中过滤?
【发布时间】:2019-04-15 02:33:09
【问题描述】:

我正在尝试将带有子元素的变量传递给 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>
            );
        }
    }

【问题讨论】:

  • 你能简化代码吗?缩进太深只会让我放弃阅读......
  • @hackape 对此感到抱歉。编辑了帖子,以便您可以看到 React 模态组件并更轻松地阅读它。
  • 我还没有阅读您的完整问题,但如果您正在寻找下拉菜单,那么我建议您不要自己创建自定义有许多包可以帮助您实现您的目的,并且是最好的包之一是:react-select.com/home

标签: javascript reactjs


【解决方案1】:
  1. 过滤器函数似乎是以“惰性”方式调用的。例如,当我在下拉字段中输入内容时,它只会传递我更改之前字段中的内容。

为什么你是父母总是得到 previous 值,是因为你调用 handleFilter() 的方式在反应的意义上有点“早期”。见代码 cmets:

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. 似乎我得到了匹配项,但没有任何内容添加到下拉 div 中并像我希望的那样清除它。

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

  1. setState() async-ly 计划更新到 usrSearch,尚未发生
  2. handleFilter() -&gt; filterUsers()sync-ly 更新 #userDropdown 的 DOM(是的,确实发生了)
  3. 计划的setState() 现在发生了,所以usrSearch 得到更新并且AssignModal 重新渲染
  4. 重新渲染只是将您的 jQuery DOM 更新覆盖为空

我并不是说将 jQuery 与 React 混合使用总是错误的,但如果你对 React 的理解不够好,大多数情况下是错误的。


如果这完全不是我应该这样做的方式,请告诉我更好的方式。

如果您使用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) 一样读取此值,因此您最好保留父母的状态,然后将其作为道具传递给AssignModel。这部分就交给你了。

【讨论】:

  • 我是否仍会在您的答案中从 AssignModal 组件调用 filterUsers 函数?...
  • @KieranOjakangas 我认为 filterUser fn 在父级中,它实际上在 AssignModal 中吗?您没有在说明中显示该部分
  • 不,它在整个 JSX 的父级中。我只是想我需要从孩子那里得到它才能让事情发生。这就是为什么我没有看到您不推荐的“修复”的替代方法 - 在设置“usrSearch”状态的同一行调用 this.handleFilter
  • @KieranOjakangas 我没明白你的意思。我看到你打电话给this.props.filtetDrop,我假设filterDrop 打电话给filterUser。所以你确实间接打电话给filterUser,对吧?你对我的回答有什么不明白的地方?
  • 是的,这就是我要说的。
【解决方案2】:

@hackape 我终于不得不接受你的回答。我遇到了一个不同的问题,它试图用 setState 更新一个单独的 JavaScript var,而不是实际将其设置为状态。但是您在该不同问题之外的建议非常有帮助 - 非常感谢!

【讨论】:

  • 我错误地添加了这个答案 - 如果有人可以删除它,请这样做。
猜你喜欢
  • 2021-04-10
  • 2014-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多