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 的方法中。

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