redux 存值 及 取值 的操作


項目目錄

首先,一個基於React + Redux + React-Router的項目目錄可以按照我下方的圖片來構建:

其中assets目錄用於存放項目的靜態資源,如css/圖片等,src目錄則用於存放React的組件資源。

 

入口文件配置

在webpack的配置項中,我們需要一個或多個入口文件,這里我就不展示關於package.json及webpack.config.js的文件配置,最后我會提供整個項目的下載鏈接供大家參考。這里我主要介紹下入口文件index.js的配置說明。

import React from 'react'  // 引入React
import { render } from 'react-dom' // 引入render方法
import { Provider } from 'react-redux' // 利用Provider可以使我們的 store 能為下面的組件所用
import { Router, browserHistory } from 'react-router' // Browser history 是由 React Router 創建瀏覽器應用推薦的 history
import { syncHistoryWithStore } from 'react-router-redux' // 利用react-router-redux提供的syncHistoryWithStore我們可以結合store同步導航事件

import finalCreateStore from './src/store/configureStore'  //引入增強后的store
import DevTools from './src/containers/DevTools'  // 引入Redux調試工具DevTools
import reducer from './src/reducers'  // 引入reducers集合
import routes from './src/routes'   // 引入路由配置

import './assets/css/bootstrap.min.css'  // 引入樣式文件

// 給增強后的store傳入reducer
const store = finalCreateStore(reducer)

// 創建一個增強版的history來結合store同步導航事件
const history = syncHistoryWithStore(browserHistory, store)

render(
    {/* 利用Provider包裹頁面 */}
    <Provider store={store}>
        <div>
            {/* 渲染根路由 */}
            <Router history={history} routes={routes} />
            {/* 渲染調試組件 */}
            <DevTools />
        </div>
    </Provider>,
    document.getElementById('mount')
)

在入口文件中我們盡量只需要保留基本的東西,其余的配置代碼我們可以放到相應的配置文件中去,比如路由、reducers及store的配置等。這里我都把它們放置到了獨立的js中,只在入口文件中通過import引入,這樣管理和維護起來會非常方便,但也會相應增加理解的難度,然而一旦上手就會很容易。那么接下來我們再來看下store配置吧。

 

store配置

import thunk from 'redux-thunk' // redux-thunk 支持 dispatch function,並且可以異步調用它
import createLogger from 'redux-logger' // 利用redux-logger打印日志
import { createStore, applyMiddleware, compose } from 'redux' // 引入redux createStore、中間件及compose 
import DevTools from '../containers/DevTools' // 引入DevTools調試組件

// 調用日志打印方法
const loggerMiddleware = createLogger()

// 創建一個中間件集合
const middleware = [thunk, loggerMiddleware]

// 利用compose增強store,這個 store 與 applyMiddleware 和 redux-devtools 一起使用
const finalCreateStore = compose(
    applyMiddleware(...middleware),
    DevTools.instrument(),
)(createStore)

export default finalCreateStore

這里我們需要了解中間件(Middleware)的概念。middleware 是指可以被嵌入在框架接收請求到產生響應過程之中的代碼,你可以在一個項目中使用多個獨立的第三方 middleware,如上面的redux-thunk和redux-logger。詳細資料請參考官方文檔:
http://cn.redux.js.org/docs/advanced/Mid...

 

路由配置

上面的入口文件配置中我們把路由配置部分單獨放到了routes.js的文件中,這里我們就來看下其配置:

import React from 'react' // 引入react
import { Route, IndexRoute } from 'react-router' // 引入react路由
import { App, Home, Foo, Bar, Antd } from './containers' // 引入各容器組件

export default (
    <Route path="/" component={App}>
        <IndexRoute component={Home}/>
        <Route path="index" component={Home}/>
        <Route path="foo" component={Foo}/>
        <Route path="bar" component={Bar}/>
        <Route path="antd" component={Antd}/>
    </Route>
)

這里的路由配置和不使用redux時候是一樣的,唯一需要了解的是容器組件和展示組件的概念。上面配置文件中的路由加載的組件都可以認為是容器組件。
(1)顧名思義,展示組件包含在容器組件中,只用作頁面展示,不會定義數據如何讀取如何改變,只通過this.props接受數據和回調函數;
(2)而容器組件中包含各展示組件的數據,即Props,它們為展示組件或其他組件提供數據和方法。
我們應該把它們放在不同的文件夾中,以示區別,如上面“項目目錄”中的containers和components文件夾分別存放容器組件和展示組件。具體說明可以參考文章:http://www.jianshu.com/p/6fa2b21f5df3

 

根組件配置

import React, { Component } from 'react' // 引入React
import { Link } from 'react-router' // 引入Link處理導航跳轉

export default class App extends Component {
    render() {
        return(
            <div>
                <nav className="navbar navbar-default">
                    <div className="container-fluid">
                        <div className="navbar-header">
                            <span className="navbar-brand" href="#">
                                <Link to="/">Redux</Link>
                            </span>
                        </div>
                        <ul className="nav navbar-nav">
                            <li>
                                <Link to="/index" activeStyle={{color: '#555', backgroundColor: '#e7e7e7'}}>計數器</Link>
                            </li>
                            <li>
                                <Link to="/foo" activeStyle={{color: '#555', backgroundColor: '#e7e7e7'}}>靜態數據</Link>
                            </li>
                            <li>
                                <Link to="/bar" activeStyle={{color: '#555', backgroundColor: '#e7e7e7'}}>動態數據</Link>
                            </li>
                            <li>
                                <Link to="/antd" activeStyle={{color: '#555', backgroundColor: '#e7e7e7'}}>結合antd</Link>
                            </li>
                        </ul>
                    </div>
                </nav>
                <div className="panel panel-default">
                    <div className="panel-body">
                        { this.props.children }
                    </div>
                </div>
            </div>
        )
    }
}

整個根組件App.js主要渲染了整個應用的導航和可變區域,這其實和Redux沒有關系。需要注意的是to中的URL地址需要和routes.js中的path地址名稱一致。

寫到這里還沒有介紹Redux中的Action及Reducer的配置,那么接下來就來介紹下。

 

Action配置

import { INCREASE, DECREASE, GETSUCCESS, REFRESHDATA } from '../constants'  // 引入action類型名常量
import 'whatwg-fetch'  // 可以引入fetch來進行Ajax

// 這里的方法返回一個action對象
export const increase = n => {
    return {
        type: INCREASE,
        amount: n
    }
}

export const decrease = n => {
    return {
        type: DECREASE,
        amount: n
    }
}

export const refreshData = () => {
    return {
        type: REFRESHDATA
    }
}

export const getSuccess = (json) => {
    return {
        type: GETSUCCESS,
        json
    }
}

function fetchPosts() {
    return dispatch => {
        return fetch('data.json')
            .then((res) => { console.log(res.status); return res.json() })
            .then((data) => {
                dispatch(getSuccess(data))
            })
            .catch((e) => { console.log(e.message) })
        }
}

// 這里的方法返回一個函數進行異步操作
export function fetchPostsIfNeeded() {

    // 注意這個函數也接收了 getState() 方法
    // 它讓你選擇接下來 dispatch 什么
    return (dispatch, getState) => {
        return dispatch(fetchPosts())
    }
}

上面返回一個action對象的方法叫做“action 創建函數”,它就是生成action的方法,也是store數據的唯一來源。
上面返回一個函數的方法叫做“異步action”,這里使用的是Redux Thunk middleware,要引入redux-thunk這個專門的庫才能使用,這樣我們就可以實現異步Ajax請求改變狀態等功能了。

 

Reducer配置

reducers/count.js
// reducers/count.js
import { INCREASE, DECREASE, GETSUCCESS, REFRESHDATA } from '../constants' // 引入action類型常量名

// 初始化state數據
const initialState = {
    number: 1,
    lists: [
        {text: '整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於唯一一個 store 中。'}, 
        {text: '惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。'},
        {text: '為了描述 action 如何改變 state tree ,你需要編寫 reducers。'},
        {text: '就是這樣,現在你應該明白 Redux 是怎么回事了。'}
    ],
    data: []
}

// 通過dispatch action進入
export default function update(state = initialState, action) {

    // 根據不同的action type進行state的更新
    switch(action.type) {
        case INCREASE:
            return Object.assign({}, state, { number: state.number + action.amount })
            break
        case DECREASE:
            return Object.assign({}, state, { number: state.number - action.amount })
            break
        case GETSUCCESS:
            return Object.assign({}, state, { data: action.json })
        case REFRESHDATA:
            return Object.assign({}, state, { data: [] })
        default:
            return state
    }
}
reducers/index.js
// reducers/index.js
import { combineReducers } from 'redux' // 利用combineReducers 合並reducers
import { routerReducer } from 'react-router-redux' // 將routerReducer一起合並管理
import update from './count' // 引入update這個reducer

export default combineReducers({
    update,
    routing: routerReducer
})

這里我們主要需要了解如何通過combineReducers來合並reducers,同時在進入reducer方法后我們必須返回一個state的處理結果來更新state狀態,否則會報錯。還需注意的是在合並reducers的時候,需要加上routerReducer這個由“react-router-redux”提供的reducer來管理路由的狀態更新。

 

容器組件

上文提到了容器組件和展示組件的區別和含義,這里我們需要在容器組件使用connect來搭配Redux來進行狀態管理,這是很關鍵的一步。

import React, { Component, PropTypes } from 'react' // 引入React
import { connect } from 'react-redux' // 引入connect 
import List from '../components/List'  // 引入展示組件List

export default class Foo extends Component {
    render() {
    
        // 通過this.props獲取到lists的值
        const { lists } = this.props

        return(
            <div>
                <ul className="list-group">
                    {將值傳入展示組件}
                    { lists.map((e, index) => 
                        <List text={e.text} key={index}></List>
                    )}
                </ul>
            </div>
        )
    }
}

// 驗證組件中的參數類型
Foo.propTypes = {
    lists: PropTypes.arrayOf(PropTypes.shape({
        text: PropTypes.string.isRequired
    }).isRequired).isRequired
}

// 獲取state中的lists值
const getList = state => {
    return {
        lists: state.update.lists
    }
}

// 利用connect將組件與Redux綁定起來
export default connect(getList)(Foo)

在容器組件中我們需要獲取state中的初始狀態的時候,我們需要使用connect。任何一個從 connect() 包裝好的組件都可以得到一個 dispatch 方法作為組件的 props,以及得到全局 state 中所需的任何內容。connect() 的唯一參數是 selector。此方法可以從 Redux store 接收到全局的 state,然后返回組件中需要的 props。詳資料請參考文檔:http://cn.redux.js.org/docs/basics/Usage...

 

展示組件

上面的容器組件中引入了一個展示組件List,我們來看下它的代碼:

import React, { Component, PropTypes } from 'react'

export default class List extends Component {
    render() {
        return(
            <li className="list-group-item">{this.props.text}</li>
        )
    }
}

List.propTypes = {
    text: PropTypes.string.isRequired
}

從中我們可以發現,展示組件沒有connect的方法,數據是通過this.props來獲取的,這樣的方式能夠是數據的變化清晰可查,便於管理和維護。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM