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中,会造成重复请求的问题。



猜你喜欢

转载自blog.csdn.net/weixin_39612961/article/details/80560426