redux概述

redux是一个状态管理器。在react中使用redux可以帮我们实现全局的数据管理。这篇文章选择了最简单的计数器为例子,通过纯redux的使用, react跟redux配合使用、以及异步redux从渐到深介绍redux。
首先,我们要先理清楚基础的概念以及工作流程。我们需要理解的地方就是:
store.dispatch(action) => excute reducer => change state
image.png

理解store

store在我理解,它是一个管理者的概念。

1.创建store: 我们可以用combineReducers把所有的reducer传进去作为参数,当然也可以只传一个reducer

  const store = createStore(
    combineReducers({
      todos,
      counter
    })
  );

2.dispatch: 通过store.dispatch(action)可以触发对应的reducer(这里的对应的,主要依赖于reducer里面对action的type进行判断)
3.subscribe: 通过store.subscribe可以监听state的变化。

  store.subscribe(() => console.log(store.getState()));
理解reducer

reducer,它是一个修改state的函数。这里的修改,不是修改某个属性值,而是直接返回一个新的state。可以用我们的...,也可以用Object.assign。它主要负责根据action.type,修改不同的state属性值。

 const initialState = { count: 0 };
 const counter = (state = initialState, action) => {
    switch (action.type) {
      case "CUSTOM_COUNT":
        return {
          count: state.count + action.count
        };
      default:
        break;
    }
  };
理解actions

actions就是一个纯函数。在同步的action里面,它负责返回一个对象,给dispatch执行。

  function addOne() {
      return {type: 'ADD', count: 1}
  }

用一个计数器的例子帮你理解redux

reudx开发流程

我们写代码的时候,
首先确定有多少种actions,这里建议定义一个actionsType.js的文件。
然后写reducer,在reducer引入actionsType.js, 里面进行switch的时候,写成actionsType.XXX以避免拼写错误问题。
然后combine reducer,创建store
最后,写actions,引入actionsType.js,这里面的函数就只是返回一个对象的纯函数,这个对象有个type,就用actionsType.XXX,你愿意返回什么值就返回什么值,跟reducer对的上就好啦。
伪代码:
actionsType.js

 export default () => (
     {
        ADD_ONE: 'ADD_ONE'
     }
 )
 

reducer

  import actionsTypes from "./actionsType.js"
  const reducer1 = (state = {}, action) => {
     switch (action.tyep) {
        case actionsTypes.ADD_ONE:
            return {...state, count: action.count}
        default:
            return state;
     }
  }

actions

 import actionsTypes from "./actionsType.js"
 function addOne() {
       return {actionsTypes.ADD_ONE, count: 1} // 上面的reducer取的就是action.count,就是因为我们这里给它的是这样的。
 }

store

 const store = createStore(
    combineReducers({
      reducer1
    })
  );
  
  store.dispatch(addOne())// 注意dispatch的是一个对象,所以是action执行后的结果
  store.suscibe(() => console.log(store.reducer1.getState()))// 如果创建的时候不是combineReducers就可以直接store.getState()
    

下面的例子为了便于理解,就没有分那么多个文件了。

pureRedux

直接上代码,因为具体步骤解释上面的伪代码应该已经写清楚了。

import {
    createStore,
    combineReducers,
    bindActionCreators
} from "redux";
import React from "react"

function run () {

    const initialState = { count: 0 };

    // reducer
    function counter (state = initialState, action) { 
        switch (action.type) {
            case 'ADD':
                return { count: state.count + action.count} // create a new state, rather than just change the state.count
            case 'REDUCE':
                return { count: state.count - action.count}
            default:
                return state
        }
    }

    const store = createStore(combineReducers({counter}))
    store.subscribe(() => {
        console.log(store.getState())
    })

    //action
    function addOne() {
        return {type: 'ADD', count: 1}
    }

    function reduceOne() {
        return {type: 'REDUCE', count: 1}
    }

    // trigger
    store.dispatch(addOne()) // dispatch the excute result of action
    // in another way to trigger
    const dispatchReduceOne = bindActionCreators(reduceOne, store.dispatch)
    dispatchReduceOne()
}


export default () => (
    <div>
      <button onClick={run}>Run</button>
      <p>* 请打开控制台查看运行结果</p>
    </div>
  );
在react中使用redux

在react中使用redux,我们不会再用store.subscribe去监听值的变化,而是用react-reudx里面的connect方法,把我们要监听的值和要调用的action方法作为属性值传给组件。
connect方法,它是一个返回高阶组件的高阶组件。先看第一层,connect(mapStateToProps, mapDispatchToProps)返回我们可以在组件里面使用到的state值和dispatch方法。第二层就是把我们的组件传递进去,让它具备这些值跟方法。
这里有个需要讲解的地方就是bindActionCreators,它负责把actions跟dispatch绑定在一起,返回一个新的函数。这个新的函数,直接执行就相当于store.dispatch。
看源码:

 function bindActionCreator()actionCreator, dispatch) {
   return function () {
     return dispatch(actionCreator.apply(this, arguments))
   }
 }
import {
    createStore,
    combineReducers,
    bindActionCreators
} from "redux";
import React from "react"
import { connect, Provider } from "react-redux";

const initialState = { count: 0 };

function counter (state = initialState, action) { // reducer
    switch (action.type) {
        case 'ADD':
            return { count: state.count + action.count} // create a new state, rather than just change the state.count
        case 'REDUCE':
            return { count: state.count - action.count}
        default:
            return state
    }
    return state
}

const store = createStore(combineReducers({counter}))

//action
function addCount(count) {
    return {type: 'ADD', count}
}

function reduceCount(count) {
    return {type: 'REDUCE', count}
}

class counterElement extends React.Component {
    render () {
        const { count, addCount, reduceCount } = this.props
        return <Provider store={store}>
            <div>
                <p>the count is: {count}</p>
                <input type="button" value="add" onClick={() => addCount(5)}/>
                <input type="button" value="reduce" onClick={() => reduceCount(5)}/>
            </div>
        </Provider> 
    }
}

function mapStateToProps (state) {
    console.log(state)
    return {
        count: state.counter.count
    }
}


function mapDispatchToProps (dispatch) {
    return bindActionCreators({addCount, reduceCount}, dispatch)
}


const WrappedCounterElement = connect(mapStateToProps, mapDispatchToProps)(counterElement)

class counterContainer extends React.Component {
    render () {
        return <Provider store={store}>
            <WrappedCounterElement/>
        </Provider>
    }
}

export default counterContainer

异步redux

如果在action里面调用的方法是异步的,那么是需要通过redux中间件,先把这个action拦截了,等到它完成了,再dispatch出去。
这里主要是我们在创建store的时候,要使用thunk中间件。

   const store = createStore(
        rootReducer,
        applyMiddleware(
        thunkMiddleware, // 允许我们 dispatch() 函数
        loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志
        )
    )
import React from "react"
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'

function run () {
    const loggerMiddleware = createLogger()

    const actionsType = {
        REQUEST_POSTS: 'REQUEST_POSTS',
        RECEIVE_POSTS: 'RECEIVE_POSTS',
        INVALIDATE_SUBREDDIT: 'INVALIDATE_SUBREDDIT'
    }

    //reducer
    const initialState = {
        isFetching: false,
        didInvalidate: false,
        items: []
    }
    function posts(
        state = initialState,
        action
    ) {
        switch (action.type) {
            case actionsType.INVALIDATE_SUBREDDIT:
                return Object.assign({}, state, {
                    didInvalidate: true
                })
            case actionsType.REQUEST_POSTS:
                return Object.assign({}, state, {
                    isFetching: true,
                    didInvalidate: false
                })
            case actionsType.RECEIVE_POSTS:
                return Object.assign({}, state, {
                    isFetching: false,
                    didInvalidate: false,
                    items: action.posts,
                    lastUpdated: action.receivedAt
                })
            default:
            return state
        }
    }

    const rootReducer = combineReducers({
        posts
    })

    // store 
    const store = createStore(
        rootReducer,
        applyMiddleware(
        thunkMiddleware, // 允许我们 dispatch() 函数
        loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志
        )
    )


    // action
    function requestPosts(subreddit) {
        return {
            type: actionsType.REQUEST_POSTS,
            subreddit
        }
    }

    function receivePosts(subreddit, json) {
        return {
            type: actionsType.RECEIVE_POSTS,
            subreddit,
            posts: json.data.children.map(child => child.data),
            receivedAt: Date.now()
        }
    }


    function fetchPosts(subreddit) {
        return function (dispatch) {
        dispatch(requestPosts(subreddit)) // display the loading status
        return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
            .then(
                response => response.json(),
                error => console.log('An error occurred.', error)
            )
            .then(json =>
                dispatch(receivePosts(subreddit, json))
            )
        }
    }

    // excute
    store.dispatch(fetchPosts('reactjs')).then(() =>    console.log(store.getState()))
}



export default () => (
    <div>
      <button onClick={run}>Run</button>
      <p>* 请打开控制台查看运行结果</p>
    </div>
  );

supportlss
230 声望16 粉丝

引用和评论

0 条评论