redux状态管理的容器。react
// 定义常量
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
// 编写处理器函数
const initState = { num: 0 }
function reducer(state = initState, action) {
switch (action.type) {
case INCREMENT:
return { num: state.num + 1 }
case DECREMENT:
return { num: state.num - 1 }
default:
return state
}
}
// 建立容器
const store = createStore(reducer)
复制代码
reducer函数须要判断动做的类型去修改状态,须要注意的是函数必需要有返回值。此函数第一个参数是
state
状态,第二个参数是action
动做,action
参数是个对象,对象里面有一个不为undefined
的type
属性,就是根据这个属性去区分各类动做类型。redux
在组件中这样使用app
const actions = {
increment() {
return { type: INCREMENT }
},
decrement() {
return { type: DECREMENT }
}
}
class Counter extends Component {
constructor(props) {
super(props);
// 初始化状态
this.state = {
num: store.getState().num
}
}
componentDidMount() {
// 添加订阅
this.unsubscribe = store.subscribe(() => {
this.setState({ num: store.getState().num })
})
}
componentWillUnmount() {
// 取消订阅
this.unsubscribe()
}
increment = () => {
store.dispatch(actions.increment())
}
decrement = () => {
store.dispatch(actions.decrement())
}
render() {
return (
<div> <span>{this.state.num}</span> <button onClick={this.increment}>加1</button> <button onClick={this.decrement}>减1</button> </div>
);
}
}
复制代码
咱们都知道组件中的
state
和props
改变都会致使视图更新,每当容器里面的状态改变须要修改state
,此时就须要用到store
中的subscribe
订阅这个修改状态的方法,该方法的返回值是取消订阅,要修改容器中的状态要用store
中的dispatch
表示派发动做类型,store
中的getState
表示获取容器中的状态。dom
为了防止本身手动调用 store.dispatch
,通常会使用redux的这个 bindActionCreators
方法来自动绑定 dispatch
方法,用法以下。ide
let actions = {
increment() {
return { type: INCREMENT }
},
decrement() {
return { type: DECREMENT }
}
}
actions = bindActionCreators(actions, store.dispatch)
class Counter extends Component {
constructor(props) {
super(props);
// 初始化状态
this.state = {
num: store.getState().num
}
}
componentDidMount() {
// 添加订阅
this.unsubscribe = store.subscribe(() => {
this.setState({ num: store.getState().num })
})
}
componentWillUnmount() {
// 取消订阅
this.unsubscribe()
}
increment = () => {
actions.increment()
}
decrement = () => {
actions.decrement()
}
render() {
return (
<div> <span>{this.state.num}</span> <button onClick={this.increment}>加1</button> <button onClick={this.decrement}>减1</button> </div>
);
}
}
export default Counter;
复制代码
这个库是链接库,用来和react和redux进行关联的,上面使用redux的时候发现一个痛点就是要订阅设置状态的方法还要取消订阅,而react-redux却能够经过props自动完成这个功能。函数
import {Provider} from 'react-redux'
import {createStore} from 'redux'
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
const initState = { num: 0 }
function reducer(state = initState, action) {
switch (action.type) {
case INCREMENT:
return { num: state.num + 1 }
case DECREMENT:
return { num: state.num - 1 }
default:
return state
}
}
const store = createStore(reducer)
ReactDOM.render((
<Provider store={store}> <Counter /> </Provider>
), document.getElementById('root'))
复制代码
Provider
是个高阶组件,须要传入store参数做为store属性,高阶组件包裹使用状态的组件。这样就完成了跨组件属性传递。学习
import {connect} from 'react-redux'
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
let actions = {
increment() {
return { type: INCREMENT }
},
decrement() {
return { type: DECREMENT }
}
}
class Counter extends Component {
render() {
return (
<div> <span>{this.props.num}</span> <button onClick={() => this.props.increment()}>加1</button> <button onClick={() => this.props.decrement()}>减1</button> </div>
);
}
}
const mapStateToProps = state => {
return state
}
const mapDispatchToProps = actions
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
复制代码
组件中使用
connect
方法关联组件和容器,这个高阶函数,须要执行两次,第一次须要传入两个参数,第一个参数是将状态映射为属性,第二个是将action
映射为属性,第二次须要传入组件做为参数。ui
该参数是个函数返回对象的形式,参数是store中的 state
,能够用来筛选咱们须要的属性,防止组件属性太多,难以维护this
好比咱们状态是这样的{ a: 1, b: 2 }
想改为这样的{ a: 1 }
,使用以下spa
const mapStateToProps = state => {
return { a: state.a }
}
复制代码
这个方法将action中的方法映射为属性,参数是个函数返回对象的形式,参数是store中的 dispatch
,能够用来筛选action
let actions = {
increment() {
return { type: INCREMENT }
},
decrement() {
return { type: DECREMENT }
}
}
复制代码
如今action
中有两个方法,咱们只须要一个的话就能够这么作了。
const mapDispatchToProps = dispatch => {
return {
increment: (...args) => dispatch(actions.increment(...args))
}
}
复制代码
如今你已经掌握了react和react-redux两个库的使用,而且知道他们的做用分别是干什么的,那么咱们就看看原理,先学习redux原理,先写一个createStore
方法。
import createStore from './createStore'
export {
createStore
}
复制代码
回顾一下createStore
是怎么使用的,使用的时候须要传入一个处理器reducer
函数,根据动做类型修改状态而后返回状态,只有在调用dispatch
方法修改状态的时候才会执行reducer
才能获得新状态。
import isPlainObject from './utils/isPlainObject'
import ActionTypes from './utils/actionTypes'
function createStore(reducer, preloadedState) {
let currentState = preloadedState
function getState() {
return currentState
}
function dispatch(action) {
// 判断是不是纯对象
if (!isPlainObject(action)) {
throw new Error('类型错误')
}
// 计算新状态
currentState = currentReducer(currentState, action)
}
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
getState
}
}
export default createStore
复制代码
在调用 dispatch
方法的时候,须要传入一个对象,而且有个 type
属性,为了保证传入的参数的正确性,调用了isPlainObject
方法,判断是不是一个对象。
function isPlainObject (obj) {
if (typeof obj !== 'object' || obj === null) {
return false
}
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
export default isPlainObject
复制代码
该方法的原理就是判断 obj.__proto__ === Object.prototype
是否相等。
redux中还有订阅和取消订阅的方法,每当状态改变执行订阅的函数。发布订阅是咱们再熟悉不过的原理了,我就很少说了。
function createStore(reducer, preloadedState) {
let currentState = preloadedState
let currentReducer = reducer
let currentListeners = []
let nextListeners = currentListeners
// 拷贝
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
function getState() {
return currentState
}
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('类型错误')
}
// 订阅
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
function dispatch(action) {
// 判断是不是纯对象
if (!isPlainObject(action)) {
throw new Error('类型错误')
}
// 计算新状态
currentState = currentReducer(currentState, action)
// 发布
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
}
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
getState,
subscribe
}
}
复制代码
ensureCanMutateNextListeners
的做用是,若是是在 listeners
被调用期间发生订阅(subscribe)或者解除订阅(unsubscribe),在本次通知中并不会当即生效,而是在下次中生效。
代码里面有个值得注意的是调用了一次dispatch
方法,派发一次动做,目的是为了获得默认值,并且为了这个动做类型不同凡响,防止定义的类型冲突,因此redux这么来写。
const randomString = () => Math.random().toString(36).substring(7).split('').join('.')
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`
}
export default ActionTypes
复制代码
bindActionCreators
在上面已经介绍了他的做用,就是为每一个方法自动绑定dispatch
方法。
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
复制代码