Redux
1、核心概念
Redux的主要目的,即集中且可预测的管理数据。集中管理指:将所有需要管理的数据放到一个store中进行管理。可预测指:数据的变化可以预测。为了实现这个功能,Redux将数据的操作全部交给了reducer来管理,而在action中定义数据处理的动作。这样当你的数据处理动作action传过来时,store便已经知道将要对数据进行的处理。在Redux中主要有三个内容,分别是store,action以及reducer。
- 1、store:连接reducer和state,并且为处理数据提供接口,
- 2、action:描述对数据处理的动作,是store 数据的唯一来源
- 3、reducer:接收 state 和 action,并返回新的 state 的函数。
三大原则
- 单一数据源:整个应用的state被储存在一棵object tree中,并且object tree只存在于唯一一个store中。
避免数据被修改,以及确定唯一的修改数据的方法就是访问store。
- State 是只读的:唯一改变state的方法就是触发action,action是一个用于描述已发生事件的普通对象。
对store外部来说,只能访问state数据,而不能修改,确保state不被其他的方法修改,而所有的数据修改方法都被reducer集中化处理。
- 使用纯函数来执行修改:为了描述 action 如何改变 state tree,你需要编写 reducers。
action描述处理数据这件事,对数据的处理是在reducer内完成的,接收先前的state以及action,并返回新的state。
使用Redux的步骤
以counter计数器为例,
1、创建state对象
对应第一个原则,单一数据源,在Redux中所有的对象都被保存在一个单一的对象中,
在store.js文件中先定义所需的对象,如下:
const state = {
counter:99,
}
2、定义action
- 官网:Action是把数据从应用传到store的有效载荷。它是store数据的唯一来源。一般来说你会通过store.dispatch()将action传到store。
- 简单来说就是要求store处理数据的信号,对于不同的信号reducer对数据进行不同的处理。
实现加减操作,定义如下:
//实现加的increment操作:
const increment ={
type = 'INCREMENT'
}
// 减操作的action
const decrement = {
type: 'DECREMENT'
}
- action创建函数,就是创建action的方法,只是简单的返回一个action。 如下,定义的increment()函数,返回的就是简单的一个action,方法内的text可以定义每次修改的数值的多少。
//返回加操作的action
function increment(text) {
return {
type: 'INCREMENT',
text
}
}
// 减操作decrement的action
function decrement() {
return {
type: 'DECREMENT',
}
}
3、定义reducer
- reducer的职责就是根据不同的action来处理数据。根据第三条规则,使用纯函数来执行修改。
对于纯函数必须遵守以下约束。
- 不得改写传入的参数
- 不能调用系统I/O的API(如DOM操作、http请求)
- 不能调用不纯的方法(如Date.now()或者Math.random())。
对于计数器案例,定义的reducer如下,
function reducer(state = initState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + action.text }
case 'DECRMENT':
return { count: state.count - 1 }
default:
return state
}
}
需要注意
- 不能修改state,
- 在default的情况下返回旧的state
当数据量多时需要对数据进行拆分处理,可以定义多个reducer,然后进行合并。
function reducer(state = state,action){
return{
//状态:reducer(state.状态,action),
todos:todos(state.todos,action),
visibility:visibility(state.todos,action)
}
}
4、创建store,并使用
store提供了三个API接口,分别是
- getState:用于获取状态
- dispatch:用于派发action
- subscribe:当状态发生变化时执行的回调函数
//可在创建store时,也可在定义reducer时,初始化数据
const store = Redux.createStore(reducer,state)
使用回调函数,以及执行函数:
store.subscribe(() => {
console.log(`现在的状态为:${store.getState().count}`)
})
store.dispatch(increment(1))
store.dispatch(increment(3))
store.dispatch(decrment())
react-redux
- react-redux是链接react与redux的专用库。在使用时需要格外安装。学习react-redux最主要的就是学习其中的三个一,一种开发思想,一个connect方法,以及一个provide组件。
- 1、一种开发思想:组件分离思想
react-redux提出了“智能”组件和“笨拙”组件相分离的开发思想,
- “智能”组件,即容器组件,主要特点有:(1)负责管理数据和业务逻辑,不负责UI的呈现,(2)带有内部状态,(3)使用redux的API。
- “笨拙”组件,又称展示组件,特点:(1)只负责UI的呈现, 不带有任何的业务逻辑,(2)没有状态,(3)所有数据由参数(this.props)提供,(4)不适用redux的API。
- 2、一个connect方法:将“笨拙”组件,变成“智能”组件。
react-redux提供了一个connect方法,用于从“笨拙”组件生成容器组件。 其中状态,是指state数据,逻辑,是指修改state的方法.
- 3、一个provider组件:为后代组件提供store对象。 在使用connect方法时,用到了store对象的dispatch方法,那么此时我们就需要使用provide组件,将store传递过去了。
还是以counter计数器为例,但是这次使用脚手架create-react-app的方式:
//安装脚手架create-react-app:
npm i create-react-app -g
//创建项目
create-react-app counter
//安装react-redux
npm i redux react-redux
//然后启动项目
npm start
counter案例:
1、先定义组件
//定义Show组件
export default class Show extends React.Component{
render() {
return (<h2>counter:{this.props.counter}</h2>)
}
}
//定义Add组件,Sub组件与Add组件一样
export default class Add extends Component{
render() {
return (<button>+</button>)
}
}
//定义counter组件,显示内容
class Counter extends Component {
render() {
return (
<div>
<Show/>
<p><Add/> <Sub/> </p>
</div>
)
}
}
2、创建store
- 1、定义store以及action creators
//定义store
const state = {counter:6,}
//定义increment的action,定义decrement的action与其类似。
export function increment() {
return {type: 'INCREMENT'}
}
- 2、定义reducer以及合并
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1
default:
return state
}
}
//合并reducer
const reducer = combineReducers({
counter// 当名字相同时,可以省略
})
- 3、创建store
//可以在创建store时初始化数据
const store = createStore(reducer,state,)
//在使用框架,在不同的文件夹下写模块时,要记得导出以及导入你需要的数据。
export default store
- 4、使用connect转化组件 在使用connect时,需要先导入必要的方法
import { connect } from 'react-redux'
//导入actions内的所有方法,并命名为action
import * as actions from '../actions/films'
import { bindActionCreators } from 'redux';
在导入必要的方法后,才能使用。
// 定义mapStarteTopProps,建立从state到props对象的映射,接受state作为参数
function mapStarteTopProps(state) {
return {counter: state.counter}}
// 定义mapDispatchToProps,用于建立笨拙组件的参数到store.dispatch方法的映射,以dispatch为参数
function mapDispatchToProps(dispatch) {
return bindActionCreators(action, dispatch)}
// 将Counter变成容器组件并导出,需要在顶层使用 provide组件,引入store,这样在子组件内就可以使用store了。
export default connect(mapStarteTopProps, mapDispatchToProps)(Counter)
- 5、使用provider组件,传递store
//需要导入必要的包
import { Provider } from 'react-redux';
import store from './store'
import Counter from './containers/Counter';
//再使用provider方法
ReactDOM.render(
// 将store集成到react应用的顶层props里面,然后使用provider组件传递。这样各个子组件就能访问到顶层的store对象了
<Provider store={store}><Counter /></Provider>,
document.getElementById('root'));
registerServiceWorker();
- 6、在各个子组件使用方法和属性
部分修改如下:
//在Add组件内使用increment方法
export default class Add extends Component{
render() {
return (<button onClick={()=>this.props.increment()}>+</button>)
}
}
//修改Counter组件,显示数据,以及给子组件传递方法
class Counter extends Component {
render() {
return (
<div>
<Show counter={this.props.counter} />
<p><Add increment={this.props.increment} />
<Sub decrement={this.props.decrement} />
</p>
</div>
)
}
}
- 测试如下:
在react-redux中实现异步操作
- 定义异步的action creator函数
在异步action中,返回一个函数,在该函数中去调用同步的action即可。 这个函数,会自动的获取dispatch和getState。
//定义异步操作,在点击后两秒实现加操作
export function incrementAsync() {
return function (dispatch, getState) {
setTimeout(function () {
dispatch(increment());
}, 2000)
}
}
- 先前已经定义好了框架,现在只需在定义好的框架内,定义方法以及调用方法即可。
//在Counter内传递方法
<AsyncAdd incrementAsync={this.props.incrementAsync}
//在Async组件内使用方法
export default class AsyncAdd extends Component {
render() {
return (<button onClick={() => this.props.incrementAsync()}>+async</button>)
}
}
- redux默认是不支持异步action的,因此我们需要使用中间件来强化store,让Reducer 在异步操作结束后自动执行。
//安装实现异步操作的中间件redux-thunk
npm i redux-thunk -S
- 使用中间件:
//先从redux-thunk内导入需要的方法
import thunkMiddleware from "redux-thunk"
// 在createStore方法上使用使用thunkMiddleware方法强化store
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware
)(createStore)
const store = createStoreWithMiddleware(reducer,state,)
这样就可以在redux内实现异步操作了。