[react基础] redux react-redux immutable.js

本文详细介绍了React-Redux应用的模块化拆分,包括单用户模块、使用redux-thunk处理异步操作,并展示了多人开发时的模块化组织方式。同时,文章讨论了Immutable.js的集成,分析其优缺点,并提供了具体的代码示例,展示如何在Redux中使用Immutable.js提高数据处理性能。
摘要由CSDN通过智能技术生成

在这里插入图片描述
案例:

点击按钮,数字+1,初始值100
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

单用户模块化拆分

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
src/store/action.js
在这里插入图片描述

src/store/index.js
在这里插入图片描述
src/store/reducer.js
在这里插入图片描述
src/store/mutation.js
在这里插入图片描述

小分析:

在这里插入图片描述

代码

在这里插入图片描述

index.js

import React from 'react';
import ReactDOM from 'react-dom';
// 根组件
import App from './App';
import store from './store'

// 定义本项目的路由模式
// BrowserRouter  history模式
// HashRouter     hash模式
import { BrowserRouter as Router } from 'react-router-dom'

ReactDOM.render(
  <Router>
    <App />
  </Router>
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
// 引入redux的数据仓库
import store from './store'
import { incrAction } from './store/action'


class App extends Component {

  // 只执行一次
  state = {
    title: '标题',
    // store.getState() 返回的是state对象数据
    ...store.getState()
  }
  unSubscribe = null

  componentDidMount() {
    // 订阅redux中的数据是否有更改,如果有,则需要通知当前组件更新一次
    // this.setState({key:value})
    // subscribe方法返回一个函数,用来取消订阅所用
    this.unSubscribe = store.subscribe(() => this.setState(store.getState()))
    // console.log(unSubscribe);
    // store.subscribe(() => this.setState({}))
  }

  componentWillUnmount() {
    // 取消订阅
    this.unSubscribe && this.unSubscribe()
  }

  addNum = () => {
    // 通知redux中state对象中的属性count要累加对应的数值
    store.dispatch(incrAction(10))
  }

  render() {
    let { count } = this.state
    // let { count } = store.getState()
    return (
      <div>
        <h3>{count}</h3>
        <button onClick={this.addNum}>累加数据</button>
      </div>
    );
  }
}

export default App;

src/store/action.js

export const incrAction = (n = 1) => ({
  type: 'incr',
  payload: n
})

src/store/index.js

// 创建一个数据仓库
import { createStore } from 'redux'
// redux调试工具
import { composeWithDevTools } from 'redux-devtools-extension';

import reducer from './reducer';

// 导出数据仓库
export default createStore(
  reducer,
  // window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  composeWithDevTools()
)


src/store/reducer.js

import mutation from "./mutation"

// 数据reducer时,一定要给state有一个初始数据,此数据就是redux中统一数据源
const initState = {
  count: 100
}

// state只能通过reducer纯函数来修改,不是直接修改state数据,返回一个新的state
const reducer = (state = initState, { type, payload }) => {
  if (mutation[type]) {
    // 第1次它要初始化,它有一个默认的 dispatch操作@@INIT动作,
    // 它无需开发者去处理,所以要判断,去除
    // 当前的操作它存在
    return mutation[type](state, payload)
  }
  return state
}

export default reducer


src/store/mutation.js

// mutation操作
const mutation = {
  incr(state, data) {
    return {
      ...state,
      count: state.count + data
    }
  }
}

export default mutation

react-redux

npm i -S react-redux  redux

src/index.js
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
装饰器的写法:

在这里插入图片描述

下面是分析过程:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

react-redux 拆分

在这里插入图片描述
在这里插入图片描述

使用异步中间件 redux-thunk

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

code 图示版

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多人开发 模块化拆分

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

code

在这里插入图片描述

index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 根组件
import App from './App';

import { BrowserRouter as Router } from 'react-router-dom'

// redux
import { Provider } from 'react-redux'
import store from './store'

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

App.js
import React, { Component } from 'react';
import Films from './components/Films';

class App extends Component {
  render() {
    return (
      <div>
        <Films />
      </div>
    );
  }
}

export default App;

src\components\Films\index.jsx
import React, { Component } from 'react'
import connect from './connect';

@connect
class Films extends Component {

  componentDidMount() {
    this.props.addFilmAction()
  }

  render() {
    return (
      <div>
        <ul>
          {
            this.props.films.map(item => (
              <li key={item.filmId}>{item.name}</li>
            ))
          }
        </ul>
      </div>
    );
  }
}

export default Films;

src\store\index.js
import { createStore, applyMiddleware, combineReducers } from 'redux'
// combineReducers 它可以把多个reducer函数合并为一个大的reducer函数
// 引入中间件
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'

// 引入拆分后的模块的reducer
import count from '@/reducer/count'
import film from '@/reducer/film'



// 把拆分后的小的reducer合并成为一个大的reducer
const reducer = combineReducers({
  // key:value
  // key的作用为:state的命名空间名称,在redux中只有state有命名空间限制,所以在redux中的type操作名称最好不要重名,重名它会多次执行
  // value就是小的redux纯函数
  count,
  film
}) 


export default createStore(
  reducer,
  composeWithDevTools(applyMiddleware(thunk))
)
src\reducer\count.js
import * as mutation from '@/mutation/countMutation'

const initState = {
  num: 100
}

const reducer = (state = initState, { type, data }) => {
  if (mutation[type]) {
    return mutation[type](state, data)
  }
  return state
}

export default reducer
src\reducer\film.js
import * as mutation from '@/mutation/filmMutation'

const initState = {
  films: []
}

const reducer = (state = initState, { type, data }) => {
  if (mutation[type]) {
    return mutation[type](state, data)
  }
  return state
}

export default reducer
src\mutation\countMutation.js
export const addNum = (state, data) => ({
  ...state,
  num: state.num + data
})
src\mutation\filmMutation.js
export const addFilm = (state, data) => ({
  ...state,
  films: [...state.films, ...data]
})
src\utils\http.js
import axios from 'axios'

// 得到一个新的请求实例
const instance = axios.create({
  // 请求超时时间
  timeout: 10000
});

// 响应拦截器
instance.interceptors.response.use(res => res.data, err => Promise.reject(err))

// get请求
export const get = (url, config = {}) => instance.get(url, config)
// post请求
export const post = (url, data = {}, config = {}) => instance.get(url, data, config)

export const put = (url, data = {}, config = {}) => instance.put(url, data, config)

export const del = (url, data = {}, config = {}) => instance.delete(url, data, config)

src\api\config\filmConfig.js
const config = {
  film: '/api/v1/getNowPlayingFilmList?cityId=110100&pageNum=1&pageSize=10'
}


export default config
src\api\filmApi.js
import { get } from '../utils/http'
import config from './config/filmConfig'

// 电影列表
export const getFilmsApi = () => get(config.film)
src\action\filmAction.js
import { getFilmsApi } from '@/api/filmApi'

/* // 对象中的方法,在没有使用中间件之前中能返回一个json对象,现在使用了中间件可以返回一个函数
export addFilm() {
  return async function (dispatch) {
    let ret = await getFilmsApi()
    dispatch({
      type: 'addFilm',
      data: ret.data.films
    })
  }
} */
// 对象中的方法,在没有使用中间件之前中能返回一个json对象,现在使用了中间件可以返回一个函数
export const addFilmAction = () => async dispatch => {
  let ret = await getFilmsApi()
  dispatch({
    type: 'addFilm',
    data: ret.data.films
  })
}



src\components\Films\connect.js
import { connect } from 'react-redux'
// 全部导入,它得到的是一个对象
import * as actions from '@/action/filmAction'


export default connect(state => {
  // console.log(state);
  // 使用了redux模块化,所以在获取state中的数据时要有命名空间属性
  return state.film
}, actions)

redux模块化自动导入

在这里插入图片描述

immutable.js

1、概述

官网:https://immutable-js.github.io/immutable-js/

https://cloud.tencent.com/developer/section/1489374

Immutable.js出自Facebook,是最流行的不可变数据结构的实现之一。它实现了完全的持久化数据结构,使用结构共享。所有的更新操作都会返回新的值,但是在内部结构是共享的,来减少内存占用(和垃圾回收的失效)。

持久化数据结构:这里说的持久化是用来描述一种数据结构,指一个数据,在被修改时,仍然能够保持修改前的状态,即不可变类型。

结构共享:Immutable使用先进的tries(字典树)技术实现结构共享来解决性能问题,当我们对一个Immutable对象进行操作的时候,ImmutableJS会只clone该节点以及它的祖先节点,其他保持不变,这样可以共享相同的部分,大大提高性能。

惰性操作:创建的对象时其实代码块没有被执行,只是被声明了,代码在获取或修改的时候才会实际被执行

2、使用immutable优缺点

  1. 优点

降低mutable带来的复杂度

节省内存

历史追溯性

拥抱函数式编程

  1. 缺点

需要重新学习api

资源包大小增加(源码5000行左右)

容易与原生对象混淆:由于api与原生不同,混用的话容易出错

第3方组件库不支持,使用时,还需要转换

3、安装

npm i -S immutable

4、常用Api

Map(): 原生object转Map对象

const map1 = Map({ a: 1, b: 2, c: 3})

const map2 = Map({ a: 1, b: 2, c: 3})

 

console.log(map1 === map2) // false

console.log(map1.equals(map2)) // true

map.toJS() // 把map转为js对象

List(): 原生array转List对象

const list1 = List([1, 2]);

const list2 = list1.push(3, 4, 5);

// 获取值

console.log(list2.get(0));

 

const list1 = List([ 1, 2, 3 ]);

const list2 = List([ 4, 5, 6 ]);

const list3 = list2.concat(list1);

console.log(list3.toArray())

fromJS(): 原生js转immutable对象

const imState = fromJS({

 name: 'lisi',

 users: ['aa', 'bb']

})

获取数据

imState.get('name')

console.log(imState.get('users').get(1))

console.log(imState.getIn(['users', 0]))

toJS(): immutable对象转原生js 不推荐使用

const state = imState.toJS()

console.log(state);

set/setIn/update/updateIn 修改数据

const newState = imState.set('name', 'zhangsan')

const newState = imState.setIn(['name'], 'zhangsan')

const newState = imState.update('count', value => value + 1)

const newState = imState.updateIn(['count'], value => value + 1)


5、redux中集成immutable.js

安装redux-immutable

redux中利用combineReducers来合并reducer并初始化state,redux自带的combineReducers只支持state是原生js形式的,所以需要使用redux-immutable提供的combineReducers来替换原来的方法。

npm i -S redux-immutable

使用

它的合并是支持immutable对象合并

import { combineReducers } from 'redux-immutable'

把所有的reducer数据转换为immutable对象

import {fromJS} from 'immutable'

const defaultState = fromJS({})

之前的
在这里插入图片描述
现在的
在这里插入图片描述

案例加入redux-immutable—code图示版

在这里插入图片描述

src/index.js

在这里插入图片描述

App.js

在这里插入图片描述

src/store/index.js

在这里插入图片描述

src/reducer/count.js

在这里插入图片描述

src/reducer/film.js

在这里插入图片描述

src/mutation/countMutation.js

在这里插入图片描述

src/mutation/filmMutation.js

在这里插入图片描述

src\action\filmAction.js

在这里插入图片描述

src/api/filmApi.js

在这里插入图片描述

src\api\config\filmConfig.js

在这里插入图片描述

src\utils\http.js

在这里插入图片描述

src/components/Film/index.jsx

在这里插入图片描述

src/components/Film/connect.js

在这里插入图片描述
在这里插入图片描述

code代码版
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 根组件
import App from './App';

import { BrowserRouter as Router } from 'react-router-dom'

// redux
import { Provider } from 'react-redux'
import store from './store'

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

src/App.js
import React, { Component } from 'react';
import Film from './components/Film';

class App extends Component {
  render() {
    return (
      <div>
        <Film />
      </div>
    );
  }
}

export default App;

src/store/index.js
import { createStore, applyMiddleware } from 'redux'
// redux-immutable
import {combineReducers} from 'redux-immutable'

// 引入中间件
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'

// 模块化自动导入
let moduleFn = require.context('../reducer', false, /\.js$/i)
let modules = moduleFn.keys().reduce((prev, moduleKey) => {
    let key = moduleKey.match(/\.\/(\w+)\.js$/i)[1]
    let value = moduleFn(moduleKey).default
    prev[key] = value
    return prev
}, {})


const reducer = combineReducers(modules)

export default createStore(
  reducer,
  composeWithDevTools(applyMiddleware(thunk))
)
src/reducer/count.js
import { fromJS } from 'immutable'
import * as mutation from '@/mutation/countMutation'

// 初始化的数据就是一个immutable对象
const initState = fromJS({
  num: 100
})

const reducer = (state = initState, { type, data }) => {
  if (mutation[type]) {
    return mutation[type](state, data)
  }
  return state
}

export default reducer
src/reducer/film.js
import { fromJS } from 'immutable'
import * as mutation from '@/mutation/filmMutation'

// 初始化的数据就是一个immutable对象
const initState = fromJS({
  // films: []
  films: null
})

const reducer = (state = initState, { type, data }) => {
  if (mutation[type]) {
    return mutation[type](state, data)
  }
  return state
}

export default reducer


src/mutation/countMutation.js
import { fromJS } from 'immutable'

export const addNum = (state, data) => state.update('num', n => n + data)
src/mutation/filmMutation.js
import { fromJS } from 'immutable'

export const addFilm = (state, data) => state.set('films', fromJS(data))

src/action/filmAction.js
import { getFilmsApi } from '@/api/filmApi'

// 对象中的方法,在没有使用中间件之前中能返回一个json对象,
// 现在使用了中间件可以返回一个函数
export const addFilmAction = () => async dispatch => {
  let ret = await getFilmsApi()
  setTimeout(() => {
    dispatch({
      type: 'addFilm',
      // 普通的json对象
      data: ret.data.films
    })
  }, 2000);

}



src/components/Film/index.jsx
import React, { Component } from 'react';
import connect from './connect';

@connect
class Film extends Component {
  componentDidMount() {
    this.props.addFilmAction()
  }
  render() {
    return (
      <div>
        <h3>电影</h3>
        <hr />
        <ul>
          {
            // 条件渲染
            this.props.films === null
              ?
              <div>加载中...</div>
              :
              this.props.films.map(item => (
                <li key={item.filmId}>{item.name}</li>
              ))
          }
        </ul>
      </div>
    );
  }
}

export default Film;

src/components/Film/connect.jsx
import { connect } from 'react-redux'
import * as actions from '@/action/filmAction'

export default connect(state => state.get('film').toJS(), actions)




src/api/filmApi.js
import { get } from '../utils/http'
import config from './config/filmConfig'

// 电影列表
export const getFilmsApi = () => get(config.film)
src/utils/http.js
import axios from 'axios'

// 得到一个新的请求实例
const instance = axios.create({
  // 请求超时时间
  timeout: 10000
});

// 响应拦截器
instance.interceptors.response.use(res => res.data, err => Promise.reject(err))

// get请求
export const get = (url, config = {}) => instance.get(url, config)
// post请求
export const post = (url, data = {}, config = {}) => instance.get(url, data, config)

export const put = (url, data = {}, config = {}) => instance.put(url, data, config)

export const del = (url, data = {}, config = {}) => instance.delete(url, data, config)

src/api/config/filmConfig.js
const config = {
  film: '/api/v1/getNowPlayingFilmList?cityId=110100&pageNum=1&pageSize=10'
}


export default config
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值