react-搜索框组件
借鉴React文档 “React的编程思想”章节,实现一个类似百度搜索的搜索框组件
文档中的步骤为
- 构建组件层次
- 用React构建一个静态版本
- 确定state的最小表示
- 确定state的位置
- 添加反向数据流
一、构建组件层次
结构如下
-SearchBox
-SearchInput
-SearchResult
-SearchResultList
因后续望在下拉框显示“加载中”“已全部加载” “无数据”等加载状态,故在SearchResult组件下,将搜索结果列表组件SearchResultList独立出来,方便后续加入状态组件到SearchResult下
二、构建静态版本
这一步,没有交互,故暂不考虑state,数据通过props从父组件向子组件传递,组件中仅有render()方法,参照文档,定义了一个数据列表,作为props传入顶级组件SearchBox,而后,SearchBox再将其传入需要该列表的子组件,及SearchResult。这里只是用于显示静态界面,编写样式,因后续改为调用Github API获取数据,该定义的数据变量及相关的组件属性最终将被删除。
这一步我采用自上而下的方式构建。
SearchBox定义如下
class SearchBox extends React.Component { render() { return ( <div className={styles.searchBox}> <SearchInput /> <SearchResult resultList={this.props.dataset} /> </div> ); } } var DATASET = [ {name: 'Wanna Be Starting Something', id: '0'}, {name: 'Baby be Mine', id: '1'}, {name: 'The Girl is Mine', id: '2'}, {name: 'Thriller', id: '3'}, {name: 'Beat It', id: '4'}, {name: 'Bilie Jean', id: '5'} ]; ReactDOM.render( <SearchBox dataset={DATASET}/>, document.getElementById('root') );SearchResult 和 SearchResultList结构如下
可以看到 SearchResult 将属性 resultList (来自父组件)传递给它的孩子 SearchResultList 的 属性 data。后续SearchResult 会调用API接口,将结果传入SearchResultList ,即SearchResultList 只是拿到数据做渲染。
SearchResultList 渲染列表的方式是调用了map方法,将每一个数据项包装在 <li> 元素中,最后返回一个 <ul> 列表
class SearchResultList extends React.Component { render() { const resultItems = this.props.data; const listItems = resultItems.map((item) => <li key={item.id}> {item.name} </li> ); return ( <ul>{listItems}</ul> ); } } class SearchResult extends React.Component { render() { return ( <div> <SearchResultList data={this.props.resultList} /> </div> ); } }三、确定state的最小表示
和文档不同的是,后来将数据列表设置为state。原文档的初始数据作为顶级父组件的props传入,筛选结果是初始数据和用户输入计算后得出,而我最终的数据列表是服务器端数据根据用户输入筛选后返回,且是不断变换的,故设计为 SearchResult 的 state
最终的 state 是:
- Input 输入框的值
- 调用API得到的数据
- loading 状态
四、state 的位置
因 SearchInput 需要输入框的值用以显示,SearchResult 需要输入框的值用以请求数据,故将之放在需要它的组件的最小公共父组件里,即 SearchBox。搜索结果列表和 loading状态放在SearchResult下
五、添加反向数据流
因输入框的值(props 属性)来自 父组件的 state, 要想输入框响应用户输入,即显示用户的输入,input 需要设置父组件的state, 即将值传递给父组件。实现方式为父组件向子组件传递 props 进行通讯,只是父组件传递的,是作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,即输入框的值,作为参数,传递到父组件的作用域中。
参考: http://taobaofed.org/blog/2016/11/17/react-components-communication/
最终代码如下:
import $ from 'jquery'; import React from 'react'; import ReactDOM from 'react-dom'; import styles from './index.css'; import registerServiceWorker from './registerServiceWorker'; class SearchResultList extends React.Component { render() { const resultItems = this.props.data; const listItems = resultItems.map((item) => <li key={item.id}> {item.name} </li> ); return ( <ul>{listItems}</ul> ); } } class SearchResult extends React.Component { constructor(props) { super(props); this.state = { loading: true, error: null, data: null }; } componentWillUpdate() { $.ajaxSetup({ async: true }); $.getJSON('https://api.github.com/search/repositories?q='+this.props.searchText, (result) => { console.log(typeof(result)); console.log(result.items); this.setState({loading: false, data: result.items}); }); } render() { if(this.state.data) { return ( <div> <SearchResultList data={this.state.data} /> </div> ); } else { return ( <div></div> ); } } } class SearchInput extends React.Component { constructor(props) { super(props); this.handleSearchTextInputChange = this.handleSearchTextInputChange.bind(this); } handleSearchTextInputChange(e) { this.props.onSearchTextInput(e.target.value); } render() { return ( <form> <input type="text" placeholder="Search..." value={this.props.searchText} onChange={this.handleSearchTextInputChange} /> </form> ); } } class SearchBox extends React.Component { constructor(props) { super(props); this.state = { searchText: '' }; this.handleSearchTextInput = this.handleSearchTextInput.bind(this); } handleSearchTextInput(searchText) { this.setState({ searchText: searchText }); } render() { return ( <div className={styles.searchBox}> <SearchInput searchText={this.state.searchText} onSearchTextInput={this.handleSearchTextInput} /> <SearchResult searchText={this.state.searchText} /> </div> ); } } ReactDOM.render( <SearchBox />, document.getElementById('root') ); registerServiceWorker();结果如下
和在 github 的搜索结果保持一致
后续优化:
1. 将请求 url 提取出来作为props传递给顶级父组件
2. 增加搜索结果列表的搜索状态样式,分次请求
3. 还需学习组件生命周期,现数据请求和设置state放在componentWillUpdate中,会造成重复请求的问题。
- React Native(RN)-组件生命周期
- react 组件使用的小记第一天
- react vr 组件之LiveEnvCamera的使用方法
- react同级组件之间传值
- 一起来点React Native——常用组件之TextInput
- React组件生命周期
- 搜索框组件
- React组件之间的通信
- React Native 组件的默认属性Props的两种写法
- 自定义react组件:Img,图片获取失败时能显示指定的默认图片
- 深入浅出React+Redux(四:Redux - 组件的Context)
- React-router(6)父组件传 【值】 or 【函数】 给子组件
- 【React】 React的优点+实现分页组件
- React-native设置全局函数globle【适用于有组件嵌套时】
- react 父子组件互相通信
- 第07节、React组件小实例
- react-native 组件的生命周期
- React学习—组件
- react中的受控组件,非受控组件;
- 【React Native开发】React Native控件之Touchable*系列组件详解(18)