【React教学】输入框自动完成提示——250行实现

为什么说使用React就是提高生产力呢?

我也不想多做解释了,大概类如以下这样的界面,用React实现,连HTML、JS、交互,250行不到,额外使用了jQuery和lodash。

完成效果大致如下:

以下是源代码

var React = require('react');
var ReactDOM = require('react-dom');

const PropTypes = React.PropTypes;

var $ = jQuery;

class GoodsSearchInput extends React.Component {

	static defaultProps = {
		min: 2,max: 20,name: '',url: location.href,delay: 1000,debug: false,selectId: 0,selectName: '',tip: '请输入关键字'
	};

	static propTypes = {
		min: PropTypes.number,max: PropTypes.number,url: PropTypes.string,delay: PropTypes.number
	};

	constructor(props) {
		super(props);
		this.state = {
			value: this.props.value,category: '',results: {},searching: {},isHide: false,isSearch: true,selected: {
				id: this.props.selectId,name: this.props.selectName
			}
		};
	}

	tick = null;

	clearTick() {
		if (this.tick != null)
			clearTimeout(this.tick);
		return this;
	}

	resetTick() {
		this.clearTick();
		let delay = this.props.delay < 300 ? 300 : this.props.delay;
		this.tick = setTimeout(() => {
			this.search().then((result) => {
			}).catch((result) => {
			});
		},this.props.delay);
		return this;
	}

	changeInput(value) {
		if (this.state.isSearch) {
			if (value.length >= this.props.min) {
				this.resetTick();
			}
			else {
				this.clearTick();
			}
		}
		this.setState({
			value: value,isSearch: true
		});
	}

	getValue() {
		return this.state.value;
	}

	search() {
		let me = this,state = this.state,value = this.getValue(),results = state.results,searching = state.searching;
		return new Promise((resolve,reject) => {
			if (typeof results[value] !== 'undefined') {
				if (results[value] === false)
					resolve(results[value]);
				else
					reject(results[value]);
			}
			else if (!searching[value]) {
				searching[value] = 1;
				me.setState({searching: searching});
				$.ajax({
					url: this.props.url,data: {
						search: value
						// category: value
					},dataType: 'json'
				}).done(function (data) {
					if (!data.count || data.count <= 0) {
						results[value] = false;
						searching[value] = 3;
						me.setState({
							results: results,searching: searching,isHide: false
						});
						reject(false);
					}
					else {
						results[value] = data;
						searching[value] = 2;
						me.setState({
							results: results,searching: searching
						});
						resolve(results[value]);
					}
				}).fail(function () {
					results[value] = false;
					searching[value] = 3;
					me.setState({
						results: results,searching: searching
					});
					reject(false);
				});
			}
		});
	}

	hideResult() {
		this.setState({isHide: true});
	}

	showResult() {
		this.setState({isHide: false});
	}

	selectItem(row) {
		this.setState({
			value: row.name,selected: row,isHide: true,isSearch: false
		});
	}

	isSelected() {
		return this.state.selected.id > 0;
	}

	cleanSelected() {
		this.setState({
			value: '',selected: {
				id: 0,name: ''
			}
		});
	}

	renderState() {
		let value = this.getValue(),searching = this.state.searching;
		switch (searching[value]) {
			case 1 :
				return <span className="searching">{`搜索“${this.state.value}”中`}</span>;
				break;
			case 2 :
				let result = this.state.results[value];
				if (this.state.isHide)
					return <span className="success">
						{`搜索“${this.state.value}”共${result.count}条结果`}
						<a onClick={() => this.showResult()} className="show">显示结果</a>
					</span>;
				else
					return <span className="success">
						{`搜索“${this.state.value}”共${result.count}条结果`}
					</span>;
				break;
			case 3 :
				return <span className="failure">{`搜索“${this.state.value}”无结果`}</span>;
				break;
			default :
				return this.props.tip;
		}
	}

	renderResult() {
		let result = this.state.results[this.getValue()];
		if (!result || result.count <= 0) {
			return '';
		}
		else if (!this.state.isHide) {
			return <div className="search-result abs">
				<div>
					{result.data.map((row) => {
						return <div key={'search_result_' + row.id}
						            className="search-item"
						            onClick={() => this.selectItem(row)}>
							<div className="search-icon"><img src={row.img}/></div>
							<div className="search-info">
								<div><strong>{row.name}</strong></div>
								<span className="goods-price">{row.price}</span>
							</div>
						</div>;
					})}
				</div>
				<div className="search-hide" onClick={() => this.hideResult()}>隐藏</div>
			</div>;
		}
	}

	renderHidden() {
		if (this.props.name) {
			return <input type="hidden"
			              name={this.props.name}
			              value={this.state.selected.id}/>
		}
	}

	render() {
		return <div className="">
			<div className="search-input-Box">
				<div className="uk-form-icon input">
					<i className="uk-icon-search"/>
					<input type="text"
					       value={this.state.value}
					       min={this.props.min}
					       maxLength={this.props.max}
					       placeholder={`输入最少${this.props.min}个字符`}
					       onChange={(event) => this.changeInput(event.target.value)}
					       className="af-input"
					/>
				</div>
				<button type="button"
				        className="uk-button uk-button-primary"
				        title="清除选中结果"
				        disabled={!this.isSelected()}
				        onClick={() => this.cleanSelected()}>
					<i className="uk-icon-remove"/>
				</button>
			</div>
			{this.renderHidden()}
			<div className="rel">
				{this.renderResult()}
			</div>
			<div className="af-tip">
				{this.renderState()}
			</div>
		</div>;
	}
}

$.fn.goodsSearchInput = function (props) {
	if (!this.get(0))
		throw new ReferenceError('Invalid DOM Element!');
	else if (!this.prop('goods-search-input')) {
		props = props || {};
		props = _.merge(props,this.data());
		let input = ReactDOM.render(<GoodsSearchInput {...props}/>,this.get(0));
		this.prop('goods-search-input',input);
	}
	return this.prop('goods-search-input');
};

module.exports = GoodsSearchInput;

其实严格来说,还可以再优化一下,做成一个通用版本,代码也可以再少些,不过因为做的时候,是赶着项目的需求做的,所以暂时就不折腾了。

做的时候是命名为GoodsSearchInput,其实事后基本上所有自动检索部分的输入框都用了他。

忘记收了,如何调用呢?巨简单,这是js方法

<div id="test"></div>
<script type="text/javascript">
	(function($) {
		$('#test').goodsSearchInput({
			name: 'test',url: 'http://localhost/test/search_goods.PHP',value: '商业包装设计',});
	})(jQuery)
</script>

下面是标签方法

<?PHP

$goods = Goods::getCacheOne($value);
?>
<div id="<?PHP echo $attr['id']; ?>"
     goods-search-input
     class="af-min-height"
     data-url="<?PHP echo linkUri('ajax/search_goods',['supplier_id' => $this->supplier->id]); ?>"
     data-name="<?PHP echo $attr['name']; ?>"
     data-value="<?PHP echo $goods->isExists() ? $goods->name : '' ?>"
     data-select-id="<?PHP echo $goods->isExists() ? $goods->id : 0 ?>"
     data-select-name="<?PHP echo $goods->isExists() ? $goods->name : '' ?>"></div>

写完调试完上述代码,用了1个小时左右的时间,增加样式调试0.5小时。剩下来的时间,可以去b站补两集番。

用React就是这样简单,如果你还没用,你out了。

相关文章

一、前言 在组件方面react和Vue一样的,核心思想玩的就是组件...
前言: 前段时间学习完react后,刚好就接到公司一个react项目...
前言: 最近收到组长通知我们项目组后面新开的项目准备统一技...
react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...