Container redux state management.
start using
// 定义常量
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 has to decide the type of the operation to modify status should be noted that the function must have a return value. The first parameter is a function of this
state
state, the second parameter is theaction
operationaction
parameter is a target, a target which has notundefined
thetype
property is based on this property to distinguish between various types of operation.
Such use in the assembly
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>
);
}
}
复制代码
We all know the components
state
andprops
changes will cause view updates whenever the state changes inside the container needs to be modifiedstate
, then you need to usestore
thesubscribe
subscription status of this modified method, the method returns a value unsubscribe to modify container in a state of usestore
of thedispatch
representation distributed type operation,store
thegetState
representation of the acquisition state of the container.
bindActionCreators
In order to prevent a manual call themselves store.dispatch
, usually using redux of this bindActionCreators
method to automatically bind dispatch
method is used as follows.
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
This library is connected to the library, and react to associate and redux, redux use of the above, it was found a sore point is to subscribe to a set state also unsubscribe method, but it can react-redux complete this function automatically by 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
Is a higher-order components, parameters need to pass a store attribute store, higher order component package assembly for use state. This completes the cross-component property transfer.
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);
复制代码
Used in the assembly
connect
method and associated components of the container, the higher-order function, it needs to be performed twice, first need to pass two parameters, the first parameter is an attribute mapping states, the second is theaction
mapped property of secondary components need to pass as a parameter.
mapStateToProps
This parameter is a function that returns an object form, the argument is the store state
, can be used to screen attributes we need to prevent the component properties too, it is difficult to maintain
For example, our state is a { a: 1, b: 2 }
want to change this { a: 1 }
, use the following
const mapStateToProps = state => {
return { a: state.a }
}
复制代码
mapDispatchToProps
This method is a method action is mapped to the attribute, the parameter is a function that returns an object form, store the parameters dispatch
can be used to screenaction
let actions = {
increment() {
return { type: INCREMENT }
},
decrement() {
return { type: DECREMENT }
}
}
复制代码
Now action
there are two methods, we only need one, then you can do that.
const mapDispatchToProps = dispatch => {
return {
increment: (...args) => dispatch(actions.increment(...args))
}
}
复制代码
principle
createStore principle
Now that you have mastered the use of react and react-redux two libraries, and know what their roles are, and then we look at the principle, first learn redux principle, to write a createStore
method.
import createStore from './createStore'
export {
createStore
}
复制代码
Look at createStore
is how to use, when to use the need to pass a processor reducer
function, and then return to the state according to the type of action to modify, only the calling dispatch
method to modify the state of the time will perform reducer
in order to get a new state.
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
复制代码
Call the dispatch
method when the need to pass an object, and there is a type
property, in order to ensure the correctness of the incoming parameters, calls the isPlainObject
method to determine whether an object.
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
复制代码
The principle of this method is to determine obj.__proto__ === Object.prototype
whether equal.
redux there are subscription and cancel the subscription method, whenever status change function is executed subscription. Subscribe to publish our familiar principle, I will not say more.
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
The effect is, if it is in listeners
the occurrence of subscription (subscribe) or rescind the subscription period is called (unsubscribe), and does not take effect immediately in this notice, but in effect the next.
Code which has a noteworthy that calls a dispatch
method to distribute one action, the purpose is to get the default value for this type of action and different, to prevent the type of conflict defined, so redux so to write.
const randomString = () => Math.random().toString(36).substring(7).split('').join('.')
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`
}
export default ActionTypes
复制代码
bindActionCreators principle
bindActionCreators
In the above it has been introduced his role is automatically bound method for each dispatch
method.
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
}
复制代码
Reproduced in: https: //juejin.im/post/5d007603e51d455d6c0ad8fc