redux、react-redux、redux-thunk、redux调试工具
一、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