Redux与react-redux 【第六章】

redux API

  • store.getState() 获取状态
  • store.dispatch({type:’increment’,data:value*1}) 添加数据
  • store.subscribe() 订阅redux状态的更改 【检测redux的数据变化,变化就掉render, 接收一个回调函数】

简单使用 redux 【精简版】

  • npm install redux 或者 yarn add redux
  • 创建一个 sotre.js 引入 import {createStore} from 'redux'
  • 创建一个 reducer.js 【本质就是一个函数,接收两个参数:之前的状态【preState,第一次为undefined】,动作对象【action】】【作用:初始化状态和加工状态】
  • 引入为组件服务的 reducer import countReducer from './count_reducer'
  • 导出store.js export default createStore(countReducer)

    reducer.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     export default function countReducer(preState = 0, action) {
    // if(preState === undefined){preState = 0 }
    const { type, data } = action
    switch (type) {
    case 'increment':
    return preState + data
    case 'decrement':
    return preState - data
    default:
    return preState
    }
    }

    store.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    该文件专门用于暴露store对象,整个应用只有一个store对象
    */
    // createStore 创建store对象
    import {createStore} from 'redux'
    // 引入为 Count 组件服务的 reducer
    import countReducer from './count_reducer'

    export default createStore(countReducer)
    // const store = createStore(countReducer)
    // export default store

    入口文件添加 [注意:react-redux 不需要]

    1
    2
    3
    4
    // 检测redux的数据变化,渲染
    store.subscribe(() => {
    ReactDOM.render(<App />,document.getElementById('root'));
    })

简单使用 redux 【完整版】

新增文件:
        1.count_action.js 专门用于创建action对象
        2.constant.js 放置容易写错的type值

count_action.js

1
2
3
4
5
6
7
8
9
10
import { INCREMENT, DECREMENT } from './constant'
export const incrementAction = (data) => ({ type: INCREMENT, data })
export const decrementAction = (data) => ({ type: DECREMENT, data })
export const incrementAsyncAction = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(incrementAction(data))
}, time);
}
}

求和案例_redux异步action版

     (1).明确:延迟的动作不想交给组件自身,想交给action
     (2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
     (3).具体编码:
                 1).yarn add redux-thunk,并配置在store中 
                 2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
                 3).异步任务有结果后,分发一个同步的action去真正操作数据。
     (4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

store.js

1
2
3
4
5
import {createStore,applyMiddleware} from 'redux'
// 解决异步,必须有中间件处理
import thunk from 'redux-thunk'
import countReducer from './count_reducer'
export default createStore(countReducer,applyMiddleware(thunk))

actions.js

1
2
3
4
5
6
7
8
9
//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const incrementAsyncAction = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(incrementAction(data))
// store.dispatch(incrementAction(data))
}, time);
}
}

react-redux的基本使用

containers文件下,容器组件

  • yarn add react-redux
  • import { connect } from ‘react-redux’、引入UI组件、 引入action 对象
  • export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
  • mapStateToProps:映射状态,返回值是一个对象
  • mapDispatchToProps:映射操作状态的方法,返回值是一个对象
  • 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
  • mapDispatchToProps,也可以是一个对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import { connect } from 'react-redux'
    // 引入UI组件
    import CountUI from '../../components/Count'
    // 引入actions 对象
    import { incrementAction, decrementAction, incrementAsyncAction } from '../../redux/count_actions'
    // 状态
    const mapStateToProps = (state) => {
    return { count: state }
    }
    // 操作状态的方法 【方法一:mapDispatchToProps写成函数,返回操作方法】
    const mapDispatchToProps = (dispatch,store) => {
    return {
    jia: (value) => {dispatch(incrementAction(value))},
    jian: (value) => {dispatch(decrementAction(value))},
    jiaIfOdd: (value) => {
    // console.log(store.store.getState());
    if(store.store.getState() % 2 !== 0){
    dispatch(incrementAction(value))
    }
    },
    jiaAsync: (value,time) => {setTimeout(() => {
    dispatch(incrementAsyncAction(value))
    }, time)},
    }
    }
    // 【方法二:mapDispatchToProps写成对象,返回操作方法】
    export default connect(mapStateToProps, {
    jia: incrementAction,
    jian: decrementAction,
    jiaAsync: incrementAsyncAction,
    })(CountUI)

APP组件

  • 引入容器组件,删除UI组件
  • 引入store,通过props传值给UI组件

优化:让所有需要 store 的容器组件都能获取 store, [Provider组件]

  1. 在入口文件中index.js,引入 import {Provider} from 'react-redux'
  2. 引入 import store from './redux/store.js'
  3. ReactDOM.render(<Provider store={store}> <App></App></Provider>,document.getElementById('root'));
  • 优化:把UI组件合并到父容器组件里
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import React, { Component } from 'react'
    // import Count from './components/Count'
    // 引入容器组件,删除UI组件
    import CountContainer from './containers/Count'
    import store from './redux/store.js'

    export default class App extends Component {
    render() {
    return (
    <div>
    {/* 通过props传值给UI组件 */}
    <CountContainer store={store}></CountContainer>
    </div>
    )
    }
    }

components文件下,UI组件

  • UI组件:不能使用任何redux的api,只负责页面的呈现、交互等
  • 容器组件:负责和redux通信,将结果交给UI组件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import React, { Component } from 'react'
    export default class Count extends Component {
    jia = () => {
    const { value } = this.selectValue
    this.props.jia(value * 1)
    }
    jian = () => {
    const { value } = this.selectValue
    this.props.jian(value * 1)
    }
    jiaIfOdd = () => {
    const { value } = this.selectValue
    // 方法一
    // this.props.jiaIfOdd(value * 1)
    // 方法二
    if (this.props.count % 2 !== 0) {
    this.props.jia(value * 1)
    }
    }
    jiaAsync = () => {
    const { value } = this.selectValue
    this.props.jiaAsync(value * 1, 500)
    }
    render() {
    return (
    <div>
    <h2>当前值为:{this.props.count}</h2>
    <select ref={c => this.selectValue = c}>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    </select>&nbsp;
    <button onClick={this.jia}>+</button>&nbsp;
    <button onClick={this.jian}>-</button>&nbsp;
    <button onClick={this.jiaIfOdd}>奇数加</button>&nbsp;
    <button onClick={this.jiaAsync}>异步加</button>&nbsp;
    </div>
    )
    }
    }

求和案例_react-redux数据共享版

(1).定义一个Pserson组件,和Count组件通过redux共享数据。
(2).为Person组件编写:reducer、action,配置constant常量。
(3).重点:在store.js文件中,Person的reducer和Count的Reducer要使用combineReducers进行合并,
        合并后的总状态是一个对象!!!
(4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。

store.js

1
2
3
4
5
6
7
8
9
10
11
12
13
// combineReducers :整合多个 Reducer
import {createStore,applyMiddleware,combineReducers} from 'redux'
import countReducer from './reducers/count_reducer'
import personReducer from './reducers/person_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

// 整合多个 Reducer 所需要的值就在 state.personReducer 中
const allReducer = combineReducers({
countReducer,
personReducer
})
export default createStore(allReducer,applyMiddleware(thunk))

纯函数

纯函数

  • 一类特别的函数: 只要是相同的输入(实参),必定的到相同的输出(返回)
  • 约束:
    • 不得改写参数数据
    • 不会产生任何副作用,例如网络请求、输入和输出设备
    • 不能调用 Date.now() 或者 Math.random() 等不纯的方法
  • react 的reducer 函数必须是一个纯函数
    1
    2
    3
    4
    5
    <!-- name函数不是纯函数 -->
    function name(data){
    a = data // 不得改写参数数据
    }
    name(23)

使用开发者工具

  • yarn add redux-devtools-extension
  • 引入 import { composeWithDevTools } from 'redux-devtools-extension'
  • export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

    store.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // combineReducers :整合多个 Reducer
    import {createStore,applyMiddleware,combineReducers} from 'redux'
    import countReducer from './reducers/count_reducer'
    import personReducer from './reducers/person_reducer'
    //引入redux-thunk,用于支持异步action
    import thunk from 'redux-thunk'
    //redux的可视化工具,谷歌的应用商城工具
    import { composeWithDevTools } from 'redux-devtools-extension'

    // 整合多个 Reducer
    const allReducer = combineReducers({
    countReducer,
    personReducer
    })
    export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

其他写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
第一种写法、
import { createStore, applyMiddleware, compose } from 'redux' //applyMiddleware用来合并多个中间件,逗号隔开
import thunk from 'redux-thunk' //可以在action 里传入 dispatch getState
import { createLogger } from 'redux-logger' // 调用日志打印方法 collapsed是让action折叠,看着舒服点
import { composeWithDevTools } from 'redux-devtools-extension' //redux的可视化工具,谷歌的应用商城工具
import rootReducer from '../reducers' //已经写好的reducers函数

let finalCreateStore
if (process.env.NODE_ENV === 'production') { //这里判断项目环境,正式的话打印的,和可视化的中间件可以去掉
finalCreateStore = compose(applyMiddleware(thunk))(createStore)
} else if (window.__REDUX_DEVTOOLS_EXTENSION__) { //检查到有redux可视化工具就使用
finalCreateStore = compose(
applyMiddleware(thunk, createLogger()),
composeWithDevTools()
)(createStore)
} else {
finalCreateStore = compose(applyMiddleware(thunk, createLogger()))(
createStore
)
}

export default function configureStore(initialState) {
return finalCreateStore(rootReducer, initialState) //创建store,和中间件,在index.js里引用调用configureStore()方法
}


第二种写法、

import { createStore, applyMiddleware } from 'redux' //applyMiddleware用来合并多个中间件,逗号隔开
import thunk from 'redux-thunk' //可以在action 里传入 dispatch getState
import { createLogger } from 'redux-logger' // 调用日志打印方法 collapsed是让action折叠,看着舒服点
import { composeWithDevTools } from 'redux-devtools-extension' //redux的可视化工具,谷歌的应用商城工具
import rootReducer from '../reducers' //已经写好的reducers函数

let finalCreateStore
if (process.env.NODE_ENV === 'production') { //这里判断项目环境,正式的话打印的,和可视化的中间件可以去掉
finalCreateStore = [thunk]
} else if (window.__REDUX_DEVTOOLS_EXTENSION__) { //检查到有redux可视化工具就使用
finalCreateStore =[thunk, createLogger()]
} else {
finalCreateStore = [thunk, createLogger()]
}
let store = createStore(
rootReducer ,
composeWithDevTools(
applyMiddleware(...finalCreateStore)
)
)
export default store

求和案例_react-redux最终版

  • 所有变量名字要规范,尽量触发对象的简写形式。
  • reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

打包上传到服务器【模拟】

  • 下载serve库 npm i serve -g
  • serve build 运行,图标变蓝
  • npm run build 一般都是打包好后交给后端,后端部署到服务器

-------------本文结束感谢您的阅读-------------

我的传送门:个人网站GithubGitee