通過Observable解決搜尋框問題

fiveoneLei發表於2018-10-22

觀察者模式

觀察者模式也叫釋出訂閱模式. 最經典的用法就是在事件監聽裡面.

    <button onClick = "handle()">確認</button>
    handle方法訂閱了onClick事件,當使用者點選(釋出)onClick, handle方法觸發
複製程式碼

大致提一下,網上對觀察者模式的解釋有很多. Observable就是基於觀察者模式實現的

Observable方法的定義

基本特徵:

  1. 是一個函式
  2. 接受一個Observer物件
  3. 返回一個具有unsubscribe功能的函式或者物件
let ob = new Observable(obser => {
    // 這個函式是“釋出”
    obser.next()
})
// 訂閱了console.log(12)動作
ob.subscribe({
    next(){console.log(12)}
})
注:只有subscribe方法執行了才會執行"釋出"函式
複製程式碼

core-js中已對observable做了擴充套件,通過import "core-js/es7/observable" 可以使用Observable

實現Observable方法

便於理解,還是簡單的實現一個Observable吧,程式碼如下

class SubscriptionObserver {
    constructor(ob, debounceTime){
        this.ob = ob || {};
        this.debounceTime = debounceTime;
        this.unSub = false;
        this.timeout = null;
    }
    next(value){
        if (!this.unSub && this.ob.next){
            clearTimeout(this.timeout)
            this.timeout = setTimeout(() => {
                !this.unSub  &&this.ob.next(value);
                this.timeout = null;
            }, this.debounceTime)
        } else {
            this.ob.next(value);
        }
    }
    error(){
        if (!this.unSub && this.ob.error){
            this.ob.error();
        }
    }
    complete(){
        if (!this.unSub && this.complete){
            this.ob.complete();
        }
    }
    unsubcribe(){
        this.unSub = true;
    }
}

class Observable {
    constructor(SubscriptionOb) {
        this.SubscriptionOb = SubscriptionOb;
    }
    subscribe(ob){
        this.subOb = new SubscriptionObserver(ob, this.timeout);
        return this.SubscriptionOb(this.subOb);
    }
    unsubcribe(){
        this.subOb.unsubcribe()
        return this;
    }
    debounceTime(timeout){
        this.timeout = timeout;
        return this;
    }
}

複製程式碼

主要增加了debounceTime功能, 在debounceTime在lazy時間內只會執行最後一次next方法

搜尋框的應用

搜尋框的搜尋主要是要解決2個問題

  1. 不能在使用者輸入每個字元的時候就觸發搜尋。
  2. 伺服器的非同步返回時間不一致,先搜尋的字元可能資料可能返回比後搜尋的慢

程式碼如下

export default class Tudos extends React.Component {
    state = {
        text:""
    }
    // 搜尋關鍵字介面
    searchKeyWords(text){
        return new Promise((resolve) => {
            setTimeout(() => {
                let list = [{...}...]
                resolve(list)
            }, 2000)
        })
    }
    handleInput(dom) {
        let oba = new Observable(ob => {
            dom.oninput = e => {
                ob.next(e.target.value)
            }
        }).debounceTime(500).subscribe({
            next(value){getList(value)}
        })

    }
    render() {
        return (<div>
            <input ref = {dom => dom && this.handleInput(dom)}/>
        </div>)
    }
}

複製程式碼

總會在使用者輸入完500毫秒後執行next函式。這個上述程式碼已實現. 第二個問題還未解決!

引入RxJS

安裝

npm i @reactivex/rxjs --save;
複製程式碼

引入

import * as Rx from "@reactivex/rxjs";
複製程式碼

RxJS 當做是用來處理事件的Lodash .像這種複雜的事件處理第一個就是想到使用RxJS

具體的使用可以參考以下官方文件

程式碼如下

export default class SimpleSortableList extends Component {
    componentDidMount() {
        
    }
    // 搜尋關鍵字介面
    searchKeyWords(text){
        return new Promise((resolve) => {
            setTimeout(() => {
                let list = [{...}...]
                resolve(list)
            }, 2000)
        })
    }
    handleInput(button){
        Rx.Observable.fromEvent(button, 'input')
        .debounceTime(500)
        .switchMap(e => {
            return Rx.Observable.create(ob => {
                this.searchKeyWords(e.target.value).then(res => {
                    ob.next(list)
                })
            });
        })
        .subscribe(text => console.log(text))
    }
    render() {
        return (
            <div className={prefix}>
                <input ref = { dom => dom && this.handleInput(dom)} />
            </div>
        );
    }
}
複製程式碼
  1. 通過debounceTime延遲返回結果
  2. 通過switchMap丟棄上一個未及時返回的結果
  3. 而且整個程式碼顯得如此短小精悍

相關文章