redux、react-redux、redux-thunk、redux调试工具

react

一、redux

安装:npm install --save redux

1. 什么是redux?

        redux是一个独立专门用于做状态管理的JS库(不是react插件库)

        它可以用在react, angular, vue等项目中, 但基本与react配合使用

        作用: 集中式管理react应用中多个组件共享的状态

2. redux的工作流程图解

3. 什么情况下需要使用redux  ==>> 实际上并没有什么要求,你可以想用就用

        总体原则: 能不用就不用, 如果不用比较吃力才考虑使用

        某个组件的状态,需要共享 ==>> 组件间需要传值(复制(跨多个组件)那种,简单的props即可)

        某个状态需要在任何地方都可以拿到

        一个组件需要改变全局状态

        一个组件需要改变另一个组件的状态

4. redux的核心API(方法)

4.1 createStore() :创建包含指定reducer的store对象,即参数为reducer

       eg:

import {createStore} from 'redux'

import counter from './reducers/counter'

const store = createStore(counter) //counter是一个reducer

4.2 applyMiddleware ():应用上基于redux的中间件(插件库)  ==> 最常见的就是实现异步的插件如 redux-thunk;参数为插件名

       eg:

import {createStore, applyMiddleware} from 'redux'

import thunk from 'redux-thunk' // redux异步中间件

const store = createStore(

counter,

applyMiddleware(thunk) // 应用上异步中间件

)

4.3 combineReducers () :合并多个reducer函数,便于统一引入并使用它创建store对象

       eg:

export default combineReducers({

user,

chatUser,

chat

})

5. redux的是三个核心概念

5.1 action :标识要执行行为的对象

        包含两个属性:

                a. type: 标识属性,值为字符串,具有唯一性,必须属性

                b. xxx: 数据属性,值为任意类型,可选属性。一般使用data作为属性名(统一)

        eg:

const action = {

type: 'INCREMENT',

data: 2

}

        Action Creator(创建Action的工厂函数):统一管理action(非常有用)

                eg:const increment = (number) => ({type: 'INCREMENT', data: number})

5.2 reducer :根据老的state和action, 产生新的state的纯函数

        eg:

export default function counter(state = 0, action) {

switch (action.type) {

case 'INCREMENT':

return state + action.data

case 'DECREMENT':

return state - action.data

default:

return state

}

}

        注意:返回的是一个新的状态

                  不要直接修改原来的状态  ==> 原因在于:react的组件更新必须依靠setState方法实现,直接修改state并没有作用

5.3 store:将state,action与reducer联系在一起的对象,是redux库最核心的管理对象

        内部维护着:state、reducer

        核心方法:

                getState(): 得到state

                dispatch(action): 分发action, 触发reducer调用, 产生新的state ==> eg: store.dispatch({type:'INCREMENT', number})

                subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

        使用createStore()方法创建store:

import {createStore} from 'redux'

import counter from './reducers/counter'

const store = createStore(counter) //counter是一个reducer

6. 实例:

6.1 组件 pages/app.js

import React, {Component} from 'react';

import * as actions from '../redux/actions' //引入所有的action

export default class App extends Component{

addFun = () => { //当前state 加上下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

//通过store的dispatch分发(触发)action并传参,

this.props.store.dispatch(actions.add(number)); //分发action后,会触发reducer的调用,更新state,但是组件并未更新,因为没有在index.js里添加监听

};

downFun = () => { //当前state 减去下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

//通过store的dispatch分发(触发)action并传参,

this.props.store.dispatch(actions.down(number));

};

addIfOddFun = () => { //当前state是奇数时:当前state 加上下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

let count = this.props.store.getState();

if(count % 2 === 1){

//通过store的dispatch分发(触发)action并传参,

this.props.store.dispatch(actions.add(number));

}

};

addAsyncFun = () => { //异步调用(定时器模拟):当前state 加上下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

setTimeout(() => {

//通过store的dispatch分发(触发)action并传参, 1s以后触发

this.props.store.dispatch(actions.add(number));

}, 1000)

};

render(){

return(

<div>

<p>

运算后的state: {this.props.store.getState()}

</p>

<select ref="numSelect">

<option value="1">1</option>

<option value="2">2</option>

<option value="3">3</option>

</select>{' '}

<button onClick={this.addFun}>+</button>

{' '}

<button onClick={this.downFun}>-</button>

{' '}

<button onClick={this.addIfOddFun}>奇数时加</button>

{' '}

<button onClick={this.addAsyncFun}>异步加</button>

</div>

)

}

}

6.2 redux/action-types.js  ==> Action对象的type常量名称模块,统一管理,避免出现不同文件中都要使用,出现错误的情况 ( 可直接写在action中 )

/*

Action对象的type常量名称模块,统一管理,避免出现不同文件中都要使用,出现错误的情况

*/

// 实际项目中可定义成Object,一个大模块定义成一个对象,里面的每一个属性就是一个type

export const INCREMENT = 'increment';

export const DECREMENT = 'decrement';

6.3 redux/actions.js

/*

Action Creator(创建Action的工厂函数),统一管理action

*/

import {INCREMENT, DECREMENT} from './action-types'

//action 是一个函数,返回的是一个标识属性type的对象

export const add = (number) => ({type: INCREMENT, data:number});

export const down = (number) => ({type: DECREMENT, data:number});

6.4 redux/reducers.js  ==>> 如果有多个reducer函数,使用combineReducers函数合并

/*

根据老的state和action, 产生新的state的纯函数

*/

import {INCREMENT, DECREMENT} from './action-types'

export function counter(state=0, action) {

console.log('调用了reducer',state, action)

switch (action.type) {

case INCREMENT:

return state + action.data;

case DECREMENT:

return state - action.data;

default:

return state;

}

}

// export default combineReducers({ // 合并reducers

// counter

// })

//引入时这样即可

// import reducers from './reducers'

6.5 主文件 index.js

import React from 'react';

import ReactDOM from 'react-dom';

import {createStore} from 'redux'

import App from './pages/redux-demo/pages/app';

import {counter} from "./pages/redux-demo/redux/reducers";

const store = createStore(counter); //使用createStore方法创建了reducer为counter的store ==> 注意:这里会默认调用一次counter函数,得到默认的state

//定义监听页面更新的函数

const render = () => {

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

}

//render渲染

render();

//订阅监听,一旦reducer改变了state,将触发更新

store.subscribe(render)

6.6 效果

6.7 这样直接使用redux的缺点

        代码比较冗余,特别是在index.js 和 pages/app.js 中,都会多次使用store

        redux与react组件的代码耦合度太高,如调用action都必须通过store.dispatch

二、react-redux:一个react插件库,专门用来简化react应用中使用redux

安装:npm install --save react-redux

1. react-redux将所有组件分成两大类

        1) UI组件

                a. 只负责 UI 的呈现,不带有任何业务逻辑

                b. 通过props接收数据(一般数据和函数)

                c. 不使用任何 Redux 的 API

                d. 一般保存在components或者pages文件夹下

        2)容器组件

                a. 负责管理数据和业务逻辑,不负责UI的呈现

                b. 使用 Redux 的 API

                c. 一般保存在containers文件夹下

2. 重要的组件和API

        1)Provider:组件,让所有组件都可以得到state数据  ==> 即代替跟组件管理 store 

<Provider store={store}> 

    <App />

</Provider>

        2)connect():API,用于包装 UI 组件生成容器组件

                个人理解:用于链接组件实例和redux

                                  用于包装UI组件,从而生成容器组件

                                  返回一个新的(特殊)组件

                                  本身是一个函数

                                  所有参数将被结构(即 ... 运算)成为链接组件的props

                                  使用之后,所有由redux(store)管理的 方法/state 都可以直接在props里面获取

import { connect } from 'react-redux'

  

connect(

    

mapStateToprops,

     //将外部的数据(即state对象)转换为UI组件的标签属性

mapDispatchToProps

   //将分发action的函数转换为UI组件的标签属性

)(Counter)

3. 实例 ==> 由上面的redux版本修改而来

1)新建容器组件文件夹 containers,并新建 app.js,代码如下:

/*

包含原本App组件的容器组件

*/

import React from 'react' //react组件中必须引入,不管是否使用

import { connect } from 'react-redux'

// 引入action函数

import {add, down} from '../redux/actions'

import App from '../pages/app' //引入原本的App UI组件

// 向外暴露连接App组件的包装组件

export default connect( //参数最终都将被映射成为原App组件的props,并返回一个新的组件

//即mapStateToprops方法,是一个函数,返回的是一个对象,count是保存在store中的state的key,在UI组件中就通过这个key来访问

state => ({count: state}),

//即mapDispatchToProps方法,这样 this.props.store.dispatch(action)的操作将有 react-redux代为执行,我们直接使用this.props.action即可

{add, down}

)(App)

2) 修改pages/app.js 即原本的App组件 ==>> 可省略(connect会给原本的App组件映射props,所以需要设置props的类型检查),增加如下代码:

static propTypes = {        //这三个props都是在调用connect是传入的参数

count: PropTypes.number.isRequired,

add: PropTypes.func.isRequired,

down: PropTypes.func.isRequired

}

3)修改入口文件index.js,由Provider管理根组件(管理store并初始化渲染页面;监听state,更新组件),渲染容器组价App,不在渲染以前的UI组件App

import React from 'react'

import ReactDOM from 'react-dom'

import {createStore} from 'redux'

import {Provider} from 'react-redux'

import App from './containers/app' //引入新的容器组件

import {counter} from './redux/reducers'

// 根据counter函数创建store对象

const store = createStore(counter)

// 定义渲染根组件标签的函数

ReactDOM.render(

(

//有Provider管理store并初始化渲染页面;监听state,更新组件

<Provider store={store}>

<App/>

</Provider>

),

document.getElementById('root')

)

4)UI组件,原本的app.js

        访问store中的state   ==>> this.props.count

        触发action: this.props.store.dispatch(actions.add(number));   ==>> this.props.add(number);

                              store.dispatch这个操作有react-redux内部去执行

        全部代码:

import React, {Component} from 'react';

export default class App extends Component{

componentDidMount(){

console.log('=====this.props',this.props);

}

addFun = () => { //当前state 加上下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

//通过store的dispatch分发(触发)action并传参,

this.props.add(number); //分发action后,会触发reducer的调用,更新state,但是组件并未更新,因为没有在index.js里添加监听

};

downFun = () => { //当前state 减去下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

//通过store的dispatch分发(触发)action并传参,

this.props.down(number);

};

addIfOddFun = () => { //当前state是奇数时:当前state 加上下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

let count = this.props.count;

if(count % 2 === 1){

//通过store的dispatch分发(触发)action并传参,

this.props.add(number);

}

};

addAsyncFun = () => { //异步调用(定时器模拟):当前state 加上下拉框的值

const number = Number(this.refs.numSelect.value); //注意数据类型

setTimeout(() => {

//通过store的dispatch分发(触发)action并传参, 1s以后触发

this.props.add(number);

}, 1000)

};

render(){

return(

<div>

<p>

运算后的state: {this.props.count}

</p>

<select ref="numSelect">

<option value="1">1</option>

<option value="2">2</option>

<option value="3">3</option>

</select>{' '}

<button onClick={this.addFun}>+</button>

{' '}

<button onClick={this.downFun}>-</button>

{' '}

<button onClick={this.addIfOddFun}>奇数时加</button>

{' '}

<button onClick={this.addAsyncFun}>异步加</button>

</div>

)

}

}

注意:a. 看一下componentDidMount打印的this.props    ==>> 这三个props都是在connect传入的

               

           b. 在react开发工具中看一下结构

              

5)redux的主要代码,即 redux/action-types.js 、redux/actions.js 、redux/reducers.js 不变

三、redux-thunk :redux插件(异步中间件)

安装:npm install --save redux-thunk

1. 为什么使用异步中间件

        redux默认是不能进行异步处理的

        应用中又需要在redux中执行异步任务(ajax, 定时器)

2. 实例 ==>> 从上面的 react-redux修改

1)把生成sore的代码从入口文件 index.js 中抽离出来,redux/store.js ,代码如下:

import React from 'react'

import {createStore, applyMiddleware} from 'redux'

import thunk from 'redux-thunk'

import {counter} from './reducers'

// 根据counter函数创建store对象

export default createStore(

counter,

applyMiddleware(thunk) // 应用上异步中间件

)

2)修改入口文件 index.js 如下

import React from 'react'

import ReactDOM from 'react-dom'

import {Provider} from 'react-redux'

import App from './containers/app' //引入新的容器组件

import store from './redux/store'

// 定义渲染根组件标签的函数

ReactDOM.render(

(

<Provider store={store}>

<App/>

</Provider>

),

document.getElementById('root')

)

3)在redux/actions.js 中新增一个异步的增加action(以前的两个加、减不变),即

// 异步action creator(返回一个函数)

export const addAsync = number => {

return dispatch => {

setTimeout(() => {

dispatch(add(number)) //一秒以后调用同步的add action函数

}, 1000)

}

}

注意:addAsync本身是一个函数,返回的也是一个函数

          redux默认是同步的,action函数只能返回一个对象

          如果需要返回一个函数(redux的异步写法),必须使用异步中间件后才可以这么写

4)在containers/app.js下引入addAsync,并作为参数传入connect中

/*

包含原本App组件的容器组件

*/

import React from 'react' //react组件中必须引入,不管是否使用

import { connect } from 'react-redux'

// 引入action函数

import {add, down, addAsync} from '../redux/actions'

import App from '../pages/app' //引入原本的App UI组件

// 向外暴露连接App组件的包装组件

export default connect(

state => ({count: state}),

{add, down, addAsync}

)(App)

4)在UI组件app中调用这个异步的action

incrementAsync = () => {

const number = this.refs.numSelect.value*1 //转为number

this.props.addAsync(number)

}

四、redux调试工具

1. 安装chrome插件:Redux DevTools

2. 在项目中安装开发依赖:npm install --save-dev redux-devtools-extension

3. 编码:

import { composeWithDevTools } from 'redux-devtools-extension'

const store = createStore(

counter,

composeWithDevTools(applyMiddleware(thunk))

)

4. 效果

文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!

以上是 redux、react-redux、redux-thunk、redux调试工具 的全部内容, 来源链接: utcz.com/z/382451.html

回到顶部