在 react 项目里如何配合immutable在redux中使用

一、reducer文件的处理

先安装 immutable 与 redux-immutable 

yarn add immutable redux-immutable

我们可能会在很多地方定义子树,这就需要在大树下将它们合并,在store文件夹的 reducer.js 文件中引入

import { combineReducers } from 'redux-immutable'
取代原本的
import { combineReducers } from 'redux'
将子树合并为一个大树。
export default combineReducers({
  homepage,
  address,
})

 

在每一个子树的 reducer.js 文件中引入 fromJS
import { fromJS } from 'immutable'

将子树的state 转为 immutable 类型数据

整个 defaultState 是个Map类型,hotCity 是 List 类型

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

以在城市列表页,以 redux 维护选择的城市这个状态,其所在子树的 reducer.js 文件引入 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() 方法是异步的请求数据,如果要想异步的请求数据则需要引入 中间件,这里用的是 redux-thunk ,同时需要引入 redux 的 applyMiddleware ,将中间件加上

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import reducers from './reducers'

const store = createStore(reducers,applyMiddleware(thunk))

export default store

//这是 store 文件夹下的 index.js 文件,暴露 store

这时就可以在action时异步的请求数据,redux-thunk会检测dispatch的内容,如果是个对象就直接让其正常向后执行,如果是函数,则会在函数执行完后,再次dispatch再正常后流。

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 的 过程!

如果有问题,欢迎指出。

 

 

 

 

posted @ 2018-11-29 17:56  wertantan  阅读(923)  评论(0编辑  收藏  举报