文档章节

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

曾建凯
 曾建凯
发布于 2016/05/18 14:11
字数 931
阅读 1538
收藏 16

为什么说使用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,
			isHide: false,
			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了。

© 著作权归作者所有

曾建凯

曾建凯

粉丝 335
博文 66
码字总数 104794
作品 0
广州
技术主管
私信 提问
加载中

评论(4)

p
phoenix0519
test
曾建凯
曾建凯 博主

引用来自“Holt_Vong”的评论

3楼猪我想问下。React组件化后,在非React为主的页面混合着用怎么样啊
对了,额外的说,如果你是指如何将jsx和es6混合到原有的js中的话,我建议你可以考虑使用webpack,你可以看我这篇教程 http://my.oschina.net/janpoem/blog/677791 ,webpack能很好的整合你的前端js,包括将jsx转译为一般的js,并支持实时预览调试。
曾建凯
曾建凯 博主

引用来自“Holt_Vong”的评论

3楼猪我想问下。React组件化后,在非React为主的页面混合着用怎么样啊
你看,我用jsx写了这个组件,并且在组件的末尾部分,将输出组件的方法注入到jQuery的扩展函数中。你看:$.fn.goodsSearchInput这里。所以到页面调用直接$(el).goodsSearchInput()即可。进一步的,考虑到易用性的问题,我们可以给GoodsSearchInput定一个静态方法,其实主要就是将ReactDOM.render(React.createElement(GoodsSearchInput, props), domEl)这个复杂的调用给封装一下,让页面调用起来的时候更直观一些。在jsx中,你就可以直接:ReactDOM.render(, domEl),比起枯燥的调用React接口要好用得多。
Holt_Vong
Holt_Vong
3楼猪我想问下。React组件化后,在非React为主的页面混合着用怎么样啊
教学评估?别烦,看这!

本文属xxKarina原创,转载请注明 个人博客地址: https://xxkarina.github.io/ 需求 作为学生,总会在学期末的时候被要求教学质量评测,但是又是考试季的,老师又是在心目中都是A的,却要一个...

xxKarina
2017/12/13
0
0
手把手教你造一个基于React的markdown编辑器

手把手教你造一个基于React的markdown编辑器 前言 笔者在18年年末的时候接到一个开发任务——搭建一个AI项目的开放平台,其中的产品文档为转化为HTML格式的markdown文档。考虑到文档的即时更...

HarryChen0506
02/25
0
0
ReactJS学习笔记——组件复合及表单的处理

ReactJS学习笔记——组件复合及表单的处理 React是一个JavaScript库文件,使用它的目的在于能够解决构建大的应用和数据的实时变更。该设计使用JSX允许你在构建标签结构时充分利用JavaScript的...

小米墨客
2016/03/26
4.3K
3
从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 && HOC 探幽

本系列文章在实现一个 cpreact 的同时帮助大家理顺 React 框架的核心内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/PureComponent/HOC/...) 项目地址 从 0 到 1 实现 React 系列 —— ...

牧云云
2018/08/27
0
0
Eclipse没有了自动提示功能,alt+/”失效的解决办法

最近公司电脑上的Eclipse没有了自动提示功能,alt+/”失效,今天在网上搜索了一下结果,主要有一下几种方法: 1、此方法用于没有一点提示的情况:依次打开eclipse上面的windows ——prefere...

习惯了-想你
2016/04/18
222
0

没有更多内容

加载失败,请刷新页面

加载更多

分布式协调服务zookeeper

ps.本文为《从Paxos到Zookeeper 分布式一致性原理与实践》笔记之一 ZooKeeper ZooKeeper曾是Apache Hadoop的一个子项目,是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它...

ls_cherish
今天
4
0
redis 学习2

网站 启动 服务端 启动redis 服务端 在redis 安装目录下 src 里面 ./redis-server & 可以指定 配置文件或者端口 客户端 在 redis 的安装目录里面的 src 里面 ./redis-cli 可以指定 指定 连接...

之渊
昨天
2
0
Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
昨天
4
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
昨天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
昨天
24
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部