Immutable.js結合React的使用

immutable.js介紹

immutable.js 是 Facebook 開源的一個項目,用於實現 javascript 的數據不可變,解決引用帶來的反作用。javascript

不變的數據(Immutable Data )一旦建立就沒法更改,從而能夠簡化應用程序開發,進行防護性複製,並可使用簡單的邏輯實現高級的備忘和更改檢測技術。持久數據提供了一個可變API,該API不會就地更新數據,而是始終產生新的更新數據,也就是說,對 Immutable 對象的任何修改或添加刪除操做都會返回一個新的 Immutable 對象。java

// 原來的寫法
let foo = {a: {b: 1}};
let bar = foo;
bar.a.b = 2;
console.log(foo.a.b);  // 打印 2
console.log(foo === bar);  //  打印 true

// 使用 immutable.js 後
import Immutable from 'immutable';
foo = Immutable.fromJS({a: {b: 1}});
bar = foo.setIn(['a', 'b'], 2);   // 使用 setIn 賦值
console.log(foo.getIn(['a', 'b']));  // 使用 getIn 取值,打印 1
console.log(foo === bar);  //  打印 false
複製代碼

immutable.js幾種數據類型

immutable.js提供了一些永久不可變數據結構,包括:react

  • List:有序可重複的列表,相似js中的Array。
  • Stack: 有序列表,經過單鏈表實現,支持使用unshift()和shift()添加和刪除。
  • Map: 無序鍵值對,相似js中的Object,es6中也有map。
  • OrderedMap:有序的map,根據數據的set()進行排序。
  • Set:沒有順序不能重複的列表。
  • OrderedSet:有序的set,根據數據的add進行排序。
  • Record: 跟Immuable的Map很像,一旦構造好,就不能添加更多的屬性(key)

經常使用API

fromJS()

做用:將一個js數據轉換爲Immutable類型的數據
用法:fromJS(value, reviver)
簡介:value是要轉變的數據,reviver是要作的操做。第二個參數可不填,默認狀況會將數組準換爲List類型,將對象轉換爲Map類型,其他不作操做。git

let bar = { a: 10, b: [10, 20, 30],c: 40,d: 10, e: [10, 20, 30],f: {g:10,h:{i:20}}}
	let demo = Immutable.fromJS(bar, 
		function (key, value, path) {
		  console.log(Immutable.isKeyed(value),'key='+key,'value='+value,'path=',path)
		})
    	//打印結果
    	//false "key=b" "value=Seq [ 10, 20, 30 ]" "path=" ["b"]
        //false "key=e" "value=Seq [ 10, 20, 30 ]" "path=" ["e"]
        //true "key=h" "value=Seq { "i": 20 }" "path=" (2) ["f", "h"]
        //true "key=f" "value=Seq { "g": 10, "h": undefined }" "path=" ["f"]
        //true "key=" "value=Seq { "a": 10, "b": undefined, "c": 40, "d": 10, "e": undefined, "f": undefined }" "path=" []
複製代碼

toJS()

做用:將一個Immutable List類型數據轉換爲JS類型的數據
用法:value.toJS()es6

is()

做用:對兩個對象進行比較 用法:is(first,second) 簡介:能夠比較原始類型(如字符串和數字),Immutable.js集合(如Map和List),以及任何經過提供和方法實現的自定義對象,比較0和-0值是相同的。和js中對象的比較不一樣,在js中比較兩個對象比較的是地址,可是在Immutable中比較的是這個對象hashCode和valueOf,只要兩個對象的hashCode相等,值就是相同的,避免了深度遍歷,提升了性能。github

merge()、mergrWith()、mergeIn()、mergeDeep()、mergeDeepIn()、mergrDeepWith()、

做用:數據的合併,新數據與舊數據對比ajax

此示例爲Map結構,List與Map原理相同:npm

const Map1 = Immutable.fromJS({a:111,b:222,c:{d:333,e:444}});
 const Map2 = Immutable.fromJS({a:111,b:222,c:{e:444,f:555}});
 const Map3 = Map1.merge(Map2); //舊數據中不存在的屬性直接添加,就數據中已存在的屬性用新數據中的覆蓋
  //Map {a:111,b:222,c:{e:444,f:555}}
 const Map4 = Map1.mergeDeep(Map2); //深合併,新舊數據中同時存在的的屬性爲新舊數據合併以後的數據
  //Map {a:111,b:222,c:{d:333,e:444,f:555}}
 const Map5 = Map1.mergeWith((oldData,newData,key)=>{ //自定義淺合併,可自行設置某些屬性的值
      if(key === 'a'){
        return 666;
      }else{
        return newData
      }
    },Map2);
  //Map {a:666,b:222,c:{e:444,f:555}}
複製代碼

get() 、 getIn()

做用:獲取數據結構中的數據redux

has() 、 hasIn()

做用:判斷是否存在某一個key數組

includes()

做用:判斷是否存在某一個value

  • 使用 Immutable.fromJS 而不是 Immutable.MapImmutable.List 來建立對象,這樣能夠避免 Immutable 和原生對象間的混用,例如,Map & Immutable.Map。
  • Immutable 中的 Map 和 List 雖對應原生 Object 和 Array,但操做很是不一樣,好比你要用 map.get('key') 而不是 map.keyarray.get(0) 而不是 array[0]。另外 Immutable 每次修改都會返回新對象,也很容易忘記賦值。

immutable.js結合react的項目應用

安裝redux-immutable的命令 npm install redux-immutable。
安裝immutable的命令 npm install immutable。 redux-immutable提供一個combineReducers()函數,將stroe中最外層的reducer中的state轉化爲immutable對象(這裏涉及到reducer的拆分,拆分用到了與redux中同名的combineReducers()方法)

好比如今我建立了一個Header的組件(代碼有刪減)

import React,{Component} from 'react';
import {connect} from 'react-redux';
import {CSSTransition} from 'react-transition-group';
class Header extends Component{
    render() {
        const {home,focused,list,login,handleInputFocus,handleInputBlur,handleHomeActive,logout} = this.props
        return (
            <HeaderWrapper>
                
                    <SearchWrapper>
                        <CSSTransition
                            in={focused}
                            timeout={200}
                            classNames="slide"
                        >
                            <NavSearch
                                className={focused?'focused':''}
                                onFocus={()=>handleInputFocus(list)}
                            />
                        </CSSTransition>
                        {this.getListArea()}
                    </SearchWrapper>
            </HeaderWrapper>
        )
    }
    getListArea() {
        const {focused,list,page,totalPage} = this.props;
        const newList = list.toJS()   //immutable對象裝換成js對象
        const pageList = []
        //ajax請求發送獲取數據以後再顯示,保證key值得有效
        if(newList.length){
            for(let i=(page-1)*10;i<page*10;i++) {
                pageList.push(
                    <SearchInfoItem key={newList[i]}>{newList[i]}</SearchInfoItem>
                )
            }
        }
        if(focused) { //鼠標聚焦輸入框時展現熱門搜索的列表
            return (
                <SearchInfo>
                    <SearchInfoTitle>
                        熱門搜索
                    </SearchInfoTitle>
                    <SearchInfoList>
                        {pageList}
                    </SearchInfoList>
                </SearchInfo>
            )
        }else{
            return null
        }
    }
}

const mapState = (state)=>{
    return {
        home:state.getIn(['header','home']),  //獲取state中保存的數據
        focused:state.getIn(['header','focused']),
        list:state.getIn(['header','list']),
    }
}
const mapDispatch = (dispatch)=>{
    return {
        handleHomeActive(value){
            dispatch(changeHomeActive(value))
        },
        handleInputFocus(list) {
            (list.size===0) && dispatch(actionCreators.getList())
            dispatch(actionCreators.searchFocus())
        },
        
    }
}

export default connect(mapState,mapDispatch)(Header)
}
複製代碼

Header組件目錄下的reducer.js的內容

import * as constants from './constants'
import {fromJS} from 'immutable'
const defaultState = fromJS({ //將數據轉化成immutable數據
    home:true,
    focused:false,
    mouseIn:false,
    list:[],
    page:1,
    totalPage:1
})
export default(state=defaultState,action)=>{
    switch(action.type){
        case constants.SEARCH_FOCUS:
            return state.set('focused',true) //更改immutable數據
        case constants.CHANGE_HOME_ACTIVE:
            return state.set('home',action.value)
        case constants.SEARCH_BLUR:
            return state.set('focused',false)
        case constants.CHANGE_LIST:
            // return state.set('list',action.data).set('totalPage',action.totalPage)
            //merge效率更高,執行一次改變多個數據
            return state.merge({
                list:action.data,
                totalPage:action.totalPage
            })
        case constants.MOUSE_ENTER:
            return state.set('mouseIn',true)
        case constants.MOUSE_LEAVE:
            return state.set('mouseIn',false)
        case constants.CHANGE_PAGE:
            return state.set('page',action.page)
        default:
            return state
    }
}
複製代碼

根目錄下的reducer.js文件

import {combineReducers} from 'redux-immutable'
import {reducer as headerReducer} from '../common/Header/store'
//當項目中有多個組件時,reducer能夠拆分爲多個reducer,而後在最外層使用combineReducers將各個reducer組合在一塊兒。這樣代碼邏輯比較清晰
const reducer = combineReducers({
    header:headerReducer,
})

export default reducer
複製代碼

引用的文章:

官方文檔

騰訊雲文檔

Immutable 詳解及 React 中實踐

相關文章
相關標籤/搜索