1. 程式人生 > >在 react 專案裡應用 immutable 對 redux 進行處理,對 List 資料渲染的問題

在 react 專案裡應用 immutable 對 redux 進行處理,對 List 資料渲染的問題

一、reducer檔案的處理

先安裝 immutable 與 redux-immutable 

yarn add immutable redux-immutable

安裝好後,要在大樹下將子樹合併,在store資料夾的 reducer.js 檔案中引入

import { combineReducers } from 'redux-immutable'
取代原本的
import { combineReducers } from 'redux'
將子樹合併為一個大樹。   在每一個子樹的 reducer.js 檔案中引入 fromJS
import { fromJS } from 'immutable'

將子樹的state 轉為 immutable 型別資料

整個是個Map型別,hotCity 是 List 型別

const defaultState = fromJS({
  hotCity: [],
  areas: [],
  city: '全國',
})

以在城市列表頁,以 redux 維護選擇的城市這個狀態,其所在子樹的 reducer.js 檔案如import { fromJS } from 'immutable

import { fromJS } from 'immutable'

import {
  GET_CITYINFO_DATA,
  CHANGE_CITY_DATA,
} from 
'./actionTypes' const defaultState = fromJS({ hotCity: [], areas: [], city: '全國', }) export default (state=defaultState,action) => { if(action.type === GET_CITYINFO_DATA){ // return { // ...state, // hotCity: [...action.result.hotCity], // areas: [...action.result.areas] //
} let newList = state.setIn(['hotCity'],fromJS(action.result.hotCity)) return newList.setIn(['areas'],fromJS(action.result.areas)) //如果要再原來的基礎上修改List資料,就要用 updataIn方法如下 插入兩個 List // let newList = newProjectInfo.updateIn(['projectInfo'],list => list.concat(fromJS(action.result.loadMore),fromJS(action.result.loadMore))) } if(action.type === CHANGE_CITY_DATA){ // return { // ...state, // city: action.city // } return state.setIn(['city'],action.city) } return state }

其中註釋掉的是不使用 immutable 的程式碼。第二個 import 引入的是 定義的 action.type 值,可以做到避免在一個大樹下有相同的 type 值。其命名如下 :

export const GET_CITYINFO_DATA = 'address/get_cityInfo_data'

 

二、在城市列表頁引入 store 內的資料

先引入 connect

import {connect} from 'react-redux'

 

再定義 mapState 與 mapDispatch。

import {CHANGE_CITY_DATA} from 'pages/address/actionTypes'

const mapState = (state) => {
  return {
    hotCity: state.getIn(['address','hotCity']),
    areas: state.getIn(['address','areas']),
  }
}
const mapDispatch = (dispatch) => {
  return {
    loadData () {
      dispatch(loadListAsync(dispatch))
    },
    changeCity (city) {
      dispatch({
        type: CHANGE_CITY_DATA,
        city
      })
    }
  }
}

其中 loadData() 方法,請求了資料, changeCity() 方法 改變了所維護的城市狀態

loadData() 的相關程式碼如下:

import {
  GET_CITYINFO_DATA,
} from './actionTypes'

export const loadCityInfoAsync = (result) => {
  return {
    type: GET_CITYINFO_DATA,
    result
  }
}

export const loadListAsync = (dispatch) => {
  return () => {
    fetch('/api/position/city')
      .then(response => response.json())
      .then(result => {
        dispatch(loadCityInfoAsync(result))
      })
  }
}

 

三、對資料的渲染

對於 List 型別的資料,利用 map() 進行渲染

程式碼如下:

import React,{Component} from 'react'
import {connect} from 'react-redux'
import BScroll from 'better-scroll'

import {AddressContainer} from './styledComponents'
import {loadListAsync} from 'pages/address/actionCreator'
import {CHANGE_CITY_DATA} from 'pages/address/actionTypes'

const mapState = (state) => {
  return {
    hotCity: state.getIn(['address','hotCity']),
    areas: state.getIn(['address','areas']),
  }
}
const mapDispatch = (dispatch) => {
  return {
    //請求資料
    loadData () {
      dispatch(loadListAsync(dispatch))
    },
    //改變redux中所儲存的城市資訊
    changeCity (city) {
      dispatch({
        type: CHANGE_CITY_DATA,
        city
      })
    }
  }
}

class AddressContent extends Component {
  render(){
    // 將 this.props 內的內容解構出來
    let { hotCity, areas, changeCity, handleClick } = this.props
    //為在一頁顯示就沒有進一步拆分,看不慣還請見諒
    return (
      // 繫結 better-scroll 的滾動根元素
      <AddressContainer className="address-com-page" ref={el => this.scrollEl = el}>
        <div>
          <div>
            <div className="address-tit">定位城市</div>
            <div className="address-hot">
              <span>定位失敗</span>
            </div>
          </div>
          <div>
            <div className="address-tit" ref="hot">熱門城市/區域</div>
            <div className="address-hot">
              {
                // 對 hotCity 進行渲染,先判斷這個資料是否存在,存在再渲染
                // 資料是一個 List ,裡面是個 Map , 裡面的 cities 的值 是一個 List 型別,取得後直接進行map()
                hotCity.get(0) && hotCity.getIn([0,'cities']).map((v,i) => {
                  return (
                    // 繫結點選事件,選取所點選的城市 changeCity()
                    // handleClick() 是路由跳轉,跳回首頁
                    // cities 的每個子元素都是 Map ,利用 get() 來獲取值
                    <span key={ v.get('cityId') }
                    onClick={() => {
                      changeCity(v.get('name'))
                      handleClick()
                    }} >
                    { v.get('name') }</span>
                  )
                })
              }
            </div>
          </div>
          <div>
            {
              // 對按字母順序排列的城市列表進行渲染
              areas.map((v,i) => {
                return (
                  <div key={v.get('prefix')}>
                    {/* 將小寫字母轉為大寫 */}
                    <div className="address-tit">{ v.get('prefix').toUpperCase() }</div>
                    <ul className="address-detail">
                      {
                        // 渲染城市列表,並繫結點選事件
                        v.get('cities').map((v,i) => {
                          return (
                            <li key={v.get('cityId')}
                              onClick={() => {
                                changeCity(v.get('name'))
                                handleClick()
                            }}>{ v.get('name') }</li>
                          )
                        })
                      }                      
                    </ul>
                  </div>
                )
              })
            }
          </div>
        </div>
      </AddressContainer>
    )
  }
  componentDidMount(){
    // 呼叫 mapDispatch 中的loadData() 方法,獲取資料
    this.props.loadData()

    this.bScroll = new BScroll(this.scrollEl,{
      click: true,
    })
  }
}

export default connect(mapState,mapDispatch)(AddressContent)

至此城市列表頁就渲染出來了。

 

以上就是利用 immutable 處理 redux 的 過程!

如果有問題,歡迎指出。