In layman's language knowledge redux

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 statestate, the second parameter is the actionoperation actionparameter is a target, a target which has not undefinedthe typeproperty 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 stateand propschanges will cause view updates whenever the state changes inside the container needs to be modified state, then you need to use storethe subscribesubscription status of this modified method, the method returns a value unsubscribe to modify container in a state of use storeof the dispatchrepresentation distributed type operation, storethe getStaterepresentation of the acquisition state of the container.

bindActionCreators

In order to prevent a manual call themselves store.dispatch, usually using redux of this bindActionCreatorsmethod to automatically bind dispatchmethod 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'))
复制代码

ProviderIs 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 connectmethod 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 the actionmapped 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 dispatchcan be used to screenaction

let actions = {
  increment() {
    return { type: INCREMENT }
  },
  decrement() {
    return { type: DECREMENT }
  }
}
复制代码

Now actionthere 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 createStoremethod.

import createStore from './createStore'

export {
  createStore
}
复制代码

Look at createStoreis how to use, when to use the need to pass a processor reducerfunction, and then return to the state according to the type of action to modify, only the calling dispatchmethod to modify the state of the time will perform reducerin 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 dispatchmethod when the need to pass an object, and there is a typeproperty, in order to ensure the correctness of the incoming parameters, calls the isPlainObjectmethod 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.prototypewhether 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
  }
}

复制代码

ensureCanMutateNextListenersThe effect is, if it is in listenersthe 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 dispatchmethod 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

bindActionCreatorsIn the above it has been introduced his role is automatically bound method for each dispatchmethod.

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

Guess you like

Origin blog.csdn.net/weixin_34121282/article/details/93168477