redux和react-redux的学习阮一峰老师关于redux的学习链接
redux是什么
- redux是一个独立专门用于做状态管理的js库(不是react插件库,后面会有一个react-redux库,它是react的插件库)
- 它可以用在react angular vue等项目中,但是基本与react配合使用(它与react契合度比较高)
- 作用:集中式管理react应用中多个组件共享的状态
什么时候使用redux
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
redux中的几个概念
- store 这是保存数据的地方,我们整个项目中只有一个store
- state 这就是store中的数据,store中可以拥有多个state
- Action 在redux中我们不能直接操作state,如果我们组件中需要改变state,我们通过
this.store.dispath()
派发action的事件,但是我们处理事件的地方是reducersconst action={ type:'ADD_TODO', payload:'redux原理' }
- store.dispatch( )
- Reducers 处理action派发过来的事件,它只是一个纯函数,接受旧的state和action,返回一个新的state
redux的工作流程
下面这个图片是redux的工作流程图
- 我们在使用redux的时候,我们就不能在view中操作state了,如果我们组件上需要改变state,我们需要action存储事件的类型和数据,我们通过在view中使用store.dispatch()分发事件给store,在我们store收到分发的事件的时候,store需要计算,然后产生一个新的状态,但是产生新状态的过程是在reducers中。** 注意这里是产生一个新的状态,并不是把原来的旧的状态改变**
- 这个时候只执行reducers中的纯函数是不行的,即使我们已经得到了一个新的state,但是我们没有监听state,没有重新渲染,我们需要重新渲染一下(但是react-redux中我们就不需要监听render() 函数重新渲染了)
- 然后我们在虚拟dom更新中把新的状态更行到组件中
reducers要求是纯函数(返回全新的对象,不影响原对象)
- 什么是纯函数:不修改函数的输入值,不依赖于外部状态(比如数据库,DOM和全局变量),同时对于任何相同的输入有着相同的输出结果。
- 为什么是纯函数: 因为在redux中认为在原来的state上进行操作,并返回的话,并不会让React重新渲染。 完全不会有任何变化。所以这里我们使用到了纯函数
- 为什么返回一个新的对象:
Redux只通过比较新旧两个对象的存储位置来比较新旧两个对象是否相同。如果你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。因此Redux认为没有任何改变,返回的state将为旧的state。那么视图就不会改变
使用redux的文件说明,具体使用说明
- 有四个文件,分别是
actions.js
action-type.js
store.js
redeucers.js
- action文件中存放
import {INCREMENT,DECREMENT} from './action-types' export const increment = (number) =>({type:INCREMENT,data:number}) export const decrement = (number) =>({type:DECREMENT,data:number})
- action-type.js中存放
export const INCREMENT = 'INCREMENT' export const DECREMENT = 'DECREMENT'
- redeucers中存放
import {INCREMENT,DECREMENT} from './action-types' export function counter(state = 0,action){ switch(action.type){ case INCREMENT: return state+action.data case DECREMENT: return state-action.data default : return state } }
- store中存放
import { createStore } from 'redux'; import {counter} from './reducers' const store = createStore( counter ) export default store
- 上面四个文件创建好之后,我们基本就可以使用了,我们在需要改变state的地方通过store.dispatch()分发action,然后通过store中的reducers返回一个新的状态
- 我们拿到新的状态后还是不行的,因为新的状态只是返回了,我们并没有把新的状态渲染到dom上,我们此时需要在入口文件(index.js)中使用
store.subscribe(render)
监听并重新渲染dom
react-redux
- 我们在单独使用redux的时候,与react的代码耦合度太高,所以我们使用react-redux来降低耦合度
- react-redux与redux的区别:redux是一个状态管理的框架,react-redux是redux的 React 绑定库。可以理解为,react-redux是为react使用redux提供方便的存在、
- 因为我们单独使用redux的时候,我们会在react代码中,使用this.props.store,这样使用,会让我们降低代码的耦合性,并且,我们在单独使用redux的时候,我们需要在状态改变的时候,动态的重新调用render()函数,所以我们也可以使用react-redux不需要手动重新渲染
在redux的基础上使用 react-redux
- 因为我们不想要手动重新渲染,我们需要用react-redux中的Provider标签,把我们App标签包裹住,我们把创建的store传递给Provider
<Provider store = {store}>
我们需要import {Provider} from 'react-redux'
- 然后我们在分发事件的时候改变一下方式
由上面那种方式变为this.props.store.dispatch( actions.increment(number) )
this.props.increment(number)
- 上面步骤后还是不能使用的,我们需要使用connect绑定(需要
import {connect} from 'react-redux'
)export default connect( state=>({ count : state }),//state 绑定的是所有状态 {increment ,decrement} // 这个对象是我们定义的一些action事件 )(App) //这句话的意思就是 我在connect中传入app组件,然后返回一个新的组件,在这个方法中我们绑定react-redux中需要的数据,这个时候app组件不需要默认返回
react-redux中存在着两个组件类型,分别是ui组件和容器组件,我们在实际开过过程中需要区分UI组件和容器组件
展示组件 | 容器组件 | |
---|---|---|
作用 | 描述如何展现(骨架、样式) | 描述如何运行(数据获取、状态更新) |
直接使用 Redux | 否 | 是 |
数据来源 | props | 监听 Redux state |
数据修改 | 从 props 调用回调函数 | 向 Redux 派发 actions |
调用方式 | 手动 | 通常由 React Redux 生成 |
redux中的异步编程问题
- 在redux中默认不支持异步编程,我们如果需要异步编程的话,我们需要下载一个
redux-thunk
这个包
在redux中使用异步编程的过程
- 下载
redux-thunk
这个中间件包,并且在store.js
引入 - 在引入createStore这个方法的时候引入applyMiddleware这个标签,并使用
const store = createStore( counter, applyMiddleware(thunk) //应用异步中间件 )
- 创建一个异步action,它依托于一个同步的action,使用如下
export const increment = (number) =>({type:INCREMENT,data:number})
export const incrementAsync = (number) =>{
return dispatch => {
setTimeout(() => {
dispatch(increment(number))
}, 1000); //返回一个带有dispatch参数的函数,这是函数中是异步调用的过程,并且异步调用的时候,是通过dispatch(同步action)的方法进行异步调用
}
}
- 然后我们像使用action的那样,就能在redux中使用异步编程了