/Redux-DataStruct

Use data struct in redux.在redux中使用复杂数据结构

Primary LanguageJavaScript

业务需求是自己空想的,也许并没有什么用。这里的复杂数据结构是指区别与JavaScript自带的数组,对象的诸如链表,队列,树之类的数据结构。网上已经有很多JavaScript实现的各种数据结构,直接抄他们的实现。

首先看这篇文章JavaScript队列、优先队列与循环队列. 这里实现了一个优先队列。基本上,redux的数据结构是扁平的数组或者是对象,现在我们做一些不平凡的事情。为了在网页上展示效果,我们引入react-redux。

create-react-app初始化项目,index.js,App.js里面的代码无关紧要,可以跳过不看。

假设有个洗手间,一群顾客排队上。

++按钮添加VIP顾客,优先级高可以插队;+按钮是普通顾客;-号按钮是顾客进洗手间了。

./index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux'
import App from './App';

import queue from './reducers/PriorityQueue';

const myQueue = queue({});

const store = createStore(myQueue);

ReactDOM.render(  <Provider store={store}>
    <App />
</Provider>, document.getElementById('root'));

./App.js

import React, { Component } from "react";
import { connect } from "react-redux";
class TempCustomer extends Component{
    render(){
        const {dataStore} = this.props
        if(dataStore.length>0){
            return <h1>最前面的人: {dataStore[0].name}</h1>
        } else {
            return null;
        }        
    }
}
const Customer = connect(mapStateToProps)(TempCustomer);

class App extends Component {
  onAddVip = function() {
    this.props.dispatch({
      type: "enqueue",
      payload: { name: "VIP_" + Math.random(), priority: 1 }
    });
  };
  onAdd = function() {
    this.props.dispatch({
      type: "enqueue",
      payload: { name: "" + Math.random(), priority: 2 }
    });
  };
  onDelete = function() {
    this.props.dispatch({ type: "dequeue" });
  };
  render() {
    return (
      <div className="App">
        {this.props.dataStore.map((ele, index) => {
          return <p key={`item-${index}`}>{ele.name}</p>;
        })}
        <button onClick={this.onAddVip.bind(this)}>++</button>
        <button onClick={this.onAdd.bind(this)}>+</button>
        <button onClick={this.onDelete.bind(this)}>-</button>
        <Customer></Customer>
      </div>
    );
  }
}

function mapStateToProps(state, ownProps) {
  return {
    dataStore: state
  };
};

export default connect(mapStateToProps)(App);

我们参考JS实现队列的那篇文章,复制他的代码过来。由于要出发react的渲染机制,数组和对象重新给一个引用地址。所以我们用packageState函数去返回一个全新的数据。

重点 ./reducers/PriorityQueue.js

class PriorityQueue {
  dataStore = [];
  enqueue = function (ele) {
    if (this.isEmpty()) {
      //如果队列是空的,直接插入
      this.dataStore.push(ele);
    } else {
      var bAdded = false;
      for (var i = 0, len = this.dataStore.length; i < len; i++) {
        if (ele.priority < this.dataStore[i].priority) {
          this.dataStore.splice(i, 0, ele); // 循环队列,如果优先级小于这个位置元素的优先级,插入
          bAdded = true;
          break;
        }
      }
      if (!bAdded) {
        this.dataStore.push(ele); // 如果循环一圈都没有找到能插队的位置,直接插入队列尾部
      }
    }
  }
  dequeue = function () {
    return this.dataStore.shift();
  };
  isEmpty = function () {
    return this.dataStore.length === 0;
  };

}

var queue = new PriorityQueue();

function itemGetter(action) {
  return action.payload;
}

function packageState(state) {
  state.dataStore = [].concat(state.dataStore);
  return Object.assign(new PriorityQueue(), state);
}

export default function ({ initialState = queue }) {
  return function (state = initialState, action) {
    switch (action.type) {
      case "enqueue":
        state.enqueue(itemGetter(action));
        return packageState(state);
      case "dequeue":
        state.dequeue(itemGetter(action));
        return packageState(state);
      default:
        return state;
    }
  };
}

OK,那么很简单,大功告成。

最终版

这是一种比较简单直观的方式。但是在比较大的项目中,可能会为了性能,引入不可变数据,以避免深度克隆数据。 我们这里引入seamless-immutable。这里时候由于数据是read-only了,对于这个队列的侵入性非常强。我们必须把出队入队的方法抽离出来,放在reducer里面。 ./reducers/PriorityQueue.js

import Immutable from "seamless-immutable";

function isEmpty(data) {
  if(!data)return false;
  return data.length === 0;
}

function itemGetter(action) {
  return action.payload;
}

export default function ({ initialState = Immutable([]) }) {
  return function (state = initialState, action) {
    const { type } = action;
    const ele = itemGetter(action);
    if (type === "enqueue") {
      if (isEmpty(state.dataStore)) {
        //如果队列是空的,直接插入
        console.log(state.dataStore.concat([ele]));
        return state.concat([ele]);
        // this.set('dataStore', this.dataStore.concat([ele]))

      } else {
        var bAdded = false;
        const origin = state.asMutable();
        for (var i = 0, len = origin.length; i < len; i++) {
          if (ele.priority < origin[i].priority) {
            origin.splice(i, 0, ele); // 循环队列,如果优先级小于这个位置元素的优先级,插入
            bAdded = true;
            break;
          }
        }
        if (!bAdded) {
          return state.concat([ele]);
        }
        return Immutable(origin);
      }
    } else if (type === "dequeue") {
      const origin = state.asMutable();
      origin.shift();
      return Immutable(origin);
    } else {
      return state;
    }
  };
}