在 Backbone 个集合中搜索,使用 React 更新 UI

Search in Backbone Collections, Updating UI with React

我正在花时间做一些可能很简单的事情: 我想实现一个搜索栏,理想情况下是按您的类型更新项目列表。我的小应用程序使用 React 和 Backbone(用于模型和集合)。

显示列表并不太难,这一切都很完美(我使用的 mixin 基本上可以轻松检索集合):

var List = React.createClass ({

    mixins: [Backbone.React.Component.mixin],

    searchFilter: function () {
        //some filtering code here, not sure how (filter method is only for arrays...)
        }
    }

    getInitialState: function () {
        initialState = this.getCollection().map(function(model) {
            return { 
                    id: model.cid,
                    name: model.get('name'),
                    description: model.get('description')
                    }
        });
        return {
            init: initialState,
            items : []
        }
    },

    componentWillMount: function () {
        this.setState({items: this.state.init})
    },

    render: function(){
        var list = this.state.items.map(function(obj){
            return (
                    <div key={obj.id}>
                        <h2>{obj.name}</h2>
                        <p>{obj.description}</p>
                    </div>      
                )
        });
        return (
            <div className='list'>
            {list}
            </div>
        )
    }
}); 

现在我尝试使用 getInitialState 方法首先将 backbone 集合转换为 "state" 但没有成功,我的想法是通过集合的副本进行代理,然后可以保存搜索结果。为了清楚起见,我没有在这里展示我的尝试(编辑:是的,我是),有人可以指导我采用正确的方法吗?提前致谢。

有很多方法可以做到这一点,但最简单的(在我看来)是将搜索条件存储在 List 组件的状态中,并使用它来过滤 collection 中的项目得到显示。您可以使用 Backbone collection 的内置 filter 方法来执行此操作。

var List = React.createClass ({

  mixins: [Backbone.React.Component.mixin],
  getInitialState: function () {
    return {
      nameFilter: ''
    };
  },
  updateSearch: function (event) {
    this.setState({
      nameFilter: event.target.value
    });
  },
  filterItems: function (item) {
    // if we have no filter, pass through
    if (!this.state.nameFilter) return true;
    return item.name.toLowerCase().indexOf(this.state.nameFilter) > -1;
  },
  render: function(){
    var list = this.props.collection
      .filter(this.filterItems.bind(this))
      .map(function(obj){
        return (
          <div key={obj.id}>
            <h2>{obj.name}</h2>
          </div>
        )
    });

    return (
      <div className='list'>
      {list}
        <input onChange={this.updateSearch} type="text" value={this.state.nameFilter}/>
      </div>
    )
  }
});

var collection = new Backbone.Collection([
  {
    name: 'Bob'
  },
  {
    name: 'Bill'
  },
  {
    name: 'James'
  }
]);

React.render(<List collection={collection}/>, document.body);

jsbin

搜索条件可以很容易地作为道具从 parent 组件传递下来,因此搜索输入不必存在于您的 List 组件中。

最终我也找到了一个不同的解决方案(如下),但它涉及将整个集合复制到状态中,这可能不是一个好主意...

var List = React.createClass ({

    mixins: [Backbone.React.Component.mixin],

    searchFilter: function () {
        var updatedlist = this.state.init;
        var searchText = this.refs.searchbar.getDOMNode().value
        updatedlist = updatedlist.filter(function (item) {
            return  item.name.toLowerCase().search(
                searchText.toLowerCase()) !== -1
        });
        this.setState({items: updatedlist})
        }
    },

    getInitialState: function () {
        initialState = this.getCollection().map(function(model) {
            return { 
                    id: model.cid,
                    name: model.get('name'),
                    description: model.get('description')
                    }
        });
        return {
            init: initialState,
            items : []
        }
    },

    componentWillMount: function () {
        this.setState({items: this.state.init})
    },

    render: function(){
        var list = this.state.items.map(function(obj){
            return (
                    <div key={obj.id}>
                        <h2>{obj.name}</h2>
                        <p>{obj.description}</p>
                    </div>      
                )
        });
        return (
            <div className='list'>
            <input ref='searchbar' type="text" placeholder="Search" onChange={this.searchFilter}/>
            {list}
            </div>
        )
    }
});