React.js的一些淫技 原

React.js的确很好用,可是我相信,除非是整个团队,彻底使用React.js重写全部UI组件,React.js才可能用得比较顺手。多数情况下,仍是半React.js组件,半传统JS技术架构的模式。

而React.js的组件,本身是一个比较封闭的状态,除了通过setState的方式从外部和组件进行交互,并不推荐使用传统OOP的方式,对组件属性进行强行修改(真的不建议,这么做你就是找死),当然,你可以把setState方法写n个接口,这样仍然属于setState范畴。

那么问题来了,传统前端JS技术架构下,如何和React.js进行交互呢?这篇文章就针对这个问题,进行一些简单的介绍,解决思路其实都是很简单的土办法。

中间代理对象

假定,我做了一个React.js的组件,这个组件用于选定公司的ID。在组件显示上,我们需要显示一个按钮,按钮的文字是选中的公司的名称,点击按钮,可以显示挑选公司的弹出层。而按钮的后面有一个隐藏域,放的是实际的公司ID。

那么因为种种原因,这个弹出层,没有用React.js封装,而是传统的html,采用ajax的方式加载,且这个弹出层要加载的页面内容、交互都已经完成了,不想再额外用React.js重新做一个组件了。

所以这时候的需求是,点击按钮,ajax加载公司列表,然后选中公司,将数据反馈回给React.js组件。问题的难点就在最后,何如将数据反馈回React.js组件。

这时候,可以考虑使用中间代理对象的模式,将React.js的组件实例,传递给中间代理对象,然后在ajax的页面,判定中间代理对象是否具有这个React.js实例,如果存在,则执行组件实例的setState方法。

下面举个实际的例子。

初始的界面如下:

这里有一个【选择供应商】的按钮,当点击供应商按钮后,会弹出如下的界面:

这个界面是用传统的html做的,采用ajax的方式进行加载。当用户点击【选中】按钮,则会关闭这个弹出层,且将用户选定的供应商名称和ID传回给刚才的React.js的组件。

代码部分如下:

React.js的组件叫做ClientSearchInput.jsx

import * as _ from 'lodash';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

import Erp from 'page/erp/Erp';
import UI from 'module/UI';

let erp = Erp.getInstance();

let copy = false;

class ClientSearchInput extends React.Component {

	index = 0;

	constructor(props) {
		super(props);
		this.index += 1;
		this.state = {
			value: this.props.value || '',
			name: '',
		};
	}

	getColumn() {
		return this.props.column
	}

	getType() {
		let column = this.getColumn();
		if (column.type !== 'client' && column.type !== 'supplier')
			return 'client';
		return column.type;
	}

	getTypeName() {
		if (this.state.name)
			return this.state.name;
		return '选择' + (this.getType() === 'client' ? '客户' : '供应商');
	}

	getPlaceholder() {
		return '输入' + (this.getType() === 'client' ? '客户' : '供应商') + '名称或者简称';
	}

	openLayer() {
		let uri = erp.uri('ui_search_client').setData({ type: this.getType() }) + '';
		let ajax = $.ajax(uri);
		ajax.done((html) => {
			UI.modal('ui_search_client', {
				header: '搜索' + this.getTypeName(),
				content: html,
				component: this,
			}).show(html);
		});
	}

	clean() {
		this.setState({
			value: '',
			name: '',
		});
	}

	copy() {
		copy = {
			name: this.state.name,
			value: this.state.value,
		}
	}

	paste() {
		if (copy) {
			this.setState(copy);
			console.log(copy);
		}
	}

	render() {
		let editable = !!this.props.editable;
		return (
			<div>
				<div className="ui tiny buttons">
					<button type="button" disabled={!editable} className="ui button tiny blue" onClick={e => this.openLayer()}>{this.getTypeName()}</button>
					<button type="button" disabled={!this.state.value} className="ui button tiny green" onClick={e => this.copy()}>复制</button>
					<button type="button" disabled={!this.state.value} className="ui button tiny red" onClick={e => this.paste()}>粘贴</button>
					<button type="button" disabled={!this.state.value} className="ui button tiny black" onClick={e => this.clean()}>清除</button>
				</div>
				<input
					type="hidden"
					name={this.props.name}
					value={this.state.value}/>
			</div>
		);
	}
}

module.exports = ClientSearchInput;

openLayer函数部分,是触发打开弹出层的位置。UI这个类的源代码在这篇文章里面《Semantic UI带你飞——Modal》,当时因为还没开始接触React.js,所以这个Modal没有做成是React.js的组件。这里UI.modal('ui_search_client')实际上就充当了中间代理对象,当然,实际上我们也没有需要额外做什么,只是把组件实例传过去而已。

而在ajax的页面js中,重新加载回这个组件:

requirejs(['module/UI'], function(UI) {
	// 前面省略
	var modal = UI.modal('ui_search_client'), opts = modal.options;
	if (opts && opts.component) {
		parent.find('button.select-client-button').click(function() {
			var button = $(this);
			var id = button.data('id'), name = button.data('name');
			opts.component.setState({
				value: id,
				name: name
			}, function() {
				modal.hide();
			})
		});
	}
});

哦,其实一切都很简单,是不是?

对象快捷链接

现在前端开发,大多是遵循amd规范,或者npm那样的模式(这是什么md来着,忘记了),即不直接写入全局对象的环境,而采用exports的方法,将需要的元素对外暴露,所以每个js脚本等于实际上都是一个密闭的scope。我们通过暴露接口的方法,提供一个读取、写入的接口,数据存放在密闭的scope中,不直接被外部访问。

这样做,的确保证了一定的安全性,但频繁的使用接口,有时候也加大了代码调试过程中繁琐。那么现在提供一种很直接的方式。

假定读取、写入的接口,明确写入的是一个对象接口,这时候我们可以利用JS对象的特性,做直接的链接操作。

即假定有,这样一个set/get的接口:

var storage = {}

any.set = function(key, object) {
    storage[key] = object || {};
}

any.get = function(key) {
    return storage[key] || false;
}

我们在另外一个scope中,确保了某个key的对象必然存在。

var localObject = any.get('local');
if (localObject === false) {
    localObject = {}
    any.set('local', localObject);
}

这样,localObject实际上已经直接指向了storage['local'],之后,我们可以对localObject,进行直接的对象访问或操作,而在别的scope中,取回local,已经是实时更新的状态,这样是不是顿时明朗了很多呢?

这实际上是JS的好处,也是JS的弊端,可以适用于如jQuery的 $(id).prop 的方法中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章