reduxの知識を深く理解する

9667 ワード

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)

reducer関数は動作のタイプを判断して状態を修正する必要があり,関数には戻り値が必要であることに注意する.この関数の最初のパラメータはstate状態、2番目のパラメータはaction動作、actionパラメータはオブジェクトで、オブジェクトの中にはundefinedtype属性は、この属性に基づいて様々な動作タイプを区別する.
コンポーネントでこのように使用
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 (
      
{this.state.num}
); } }

コンポーネントのstateおよびpropsが変更されるとビューが更新され、コンテナ内の状態が変更されるたびに変更が必要になります.state、この場合に使用する必要がありますstoresubscribeこの変更ステータスを購読する方法で、このメソッドの戻り値は購読をキャンセルし、コンテナ内のステータスを変更するにはstoredispatchは配布動作タイプを表し、storegetStateは、取得容器中の状態を示す.
bindActionCreators store.dispatchを手動で呼び出すのを防止するために、reduxのこのbindActionCreatorsメソッドを使用してdispatchメソッドを自動的にバインドするのが一般的です.
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 (
      
{this.state.num}
); } } export default Counter;

react-redux
このライブラリは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((
  
    
  
), 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 (
      
{this.props.num}
); } } const mapStateToProps = state => { return state } const mapDispatchToProps = actions export default connect(mapStateToProps, mapDispatchToProps)(Counter);

コンポーネントでの使用connectメソッドはコンポーネントとコンテナを関連付け、この高次関数は、2回実行する必要があり、1回目は2つのパラメータを入力する必要があり、1つ目のパラメータは状態を属性にマッピングし、2つ目はactionは属性としてマッピングされ、2回目はパラメータとしてコンポーネントを入力する必要がある.
mapStateToProps
このパラメータは関数がオブジェクトを返す形式で、パラメータはstoreのstateで、私たちが必要とする属性をフィルタリングすることができて、コンポーネントの属性が多すぎて、維持しにくいことを防止します
例えば私たちの状態はこのような{ a: 1, b: 2 }で、このような{ a: 1 }に変えたいと思っています.
const mapStateToProps = state => {
  return { a: state.a }
}

mapDispatchToProps
この方法はactionのメソッドを属性にマッピングし、パラメータは関数がオブジェクトを返す形式であり、パラメータはstoreのdispatchであり、actionをフィルタリングするために使用することができる.
let actions = {
  increment() {
    return { type: INCREMENT }
  },
  decrement() {
    return { type: DECREMENT }
  }
}

現在actionには2つの方法がありますが、私たちは1つだけ必要であればそうすることができます.
const mapDispatchToProps = dispatch => {
  return {
    increment: (...args) => dispatch(actions.increment(...args))
  }
}

reduxの原理
createStoreの原理
reactとreact-reduxの2つのライブラリの使用を把握し、彼らの役割がそれぞれ何をしているかを知っています.では、原理を見て、まず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メソッドを呼び出す場合、1つのオブジェクトが入力され、typeプロパティがあり、入力されたパラメータの正確性を保証するためにisPlainObjectメソッドが呼び出され、1つのオブジェクトであるか否かが判断される.
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

この方法の原理は,この対象のプロトタイプが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の原理bindActionCreatorsは、dispatchメソッドを各メソッドに自動的にバインドする役割を上述した.
export default function bindActionCreators(actionCreators, dispatch) {
  function bindActionCreator(actionCreators, dispatch) {
    return function () {
      return dispatch(actionCreators.apply(this, arguments))
    }
  }
  if (typeof actionCreators === 'function') {
    bindActionCreator(actionCreators, dispatch)
  }
  const boundActionCreator = {}
  for (const key in actionCreators) {
    boundActionCreator[key] = bindActionCreator(actionCreators[key], dispatch)
  }
  return boundActionCreator
}