redux 使用實例

一. 純原生 redux 的使用

Redux 是常用的狀態(tài)管理容器,能夠幫助我們對一些全局變量進行有效管理凰浮。首先我抠,舉一個場景:我正在開開心心的逛淘寶商城,點進了一個淘寶店家的商品 A 的詳情頁袜茧,覺得不錯菜拓,于是點擊 ”加入購物車“,接著我又點進了一個天貓店家的商品 B 的詳情頁笛厦,也覺得不錯纳鼎,又點擊了 ”加入購物車“,最終我打開購物車發(fā)現(xiàn)有 A B 兩件商品裳凸。假設(shè)淘寶商品詳情頁和天貓商品是兩個相互獨立的組件贱鄙,那么要實現(xiàn)上述場景,購物車就應(yīng)當(dāng)是一個共享組件姨谷,此時就可以使用 redux 對購物車實現(xiàn)統(tǒng)一管理逗宁。那么以當(dāng)前這個場景,使用原生 redux 應(yīng)該怎么實現(xiàn)呢梦湘?廢話不多說疙剑,看如下實現(xiàn):

  1. 安裝 redux

    redux 支持 npm 等包管理工具,故而進入項目目錄中践叠,執(zhí)行 npm install redux --save

  2. 建立一個倉庫來存儲數(shù)據(jù)
    // store.js
    import {createStore} from 'redux'
    // 聲明一個實際的操作函數(shù)言缤,響應(yīng)操作
    const cartReducer = (state = {
        goods: []
    }, action) => {
        switch (action.type) {
            case 'add':
                return {...state, goods: [...state.goods, action.good]}
            case 'delete':
                let deletedGoodIdx = action.good.index
                let newGoods = [...state.goods]
                newGoods.splice(deletedGoodIdx, 1)
                return {...state, goods: newGoods}
            default:
                return state
        }
    }
    // 創(chuàng)建 store 實例并導(dǎo)出
    const store = createStore(cartReducer)
    export default store
    
  3. 在組件中使用 store

    讀取:store.getState() 即可獲取到 reducer 中的 state

    修改:store.dispatch(action) 通過 dispatch 一個 action 來觸發(fā) reducer 中的修改行為

    /********** App.js **************/
    import React from 'react'
    import {Link, Route} from 'react-router-dom'
    import store from './store'
    
    // 淘寶商品詳情組件
    function TbItem() {
        return (
            <div>
                <h2>這是淘寶詳情頁</h2>
                <p>當(dāng)前購物車內(nèi)商品數(shù)量:{store.getState().goods.length}</p>
             <button onClick={() => store.dispatch({type: 'add', good: {title: `淘寶商品${Date.now()}`, price: 100}})}>添加購物車</button>
         </div>
        )
    }
    
    // 天貓商品詳情組件
    function TmItem() {
        return (
            <div>
                <h2>這是天貓詳情頁</h2>
                <p>當(dāng)前購物車內(nèi)商品數(shù)量:{store.getState().goods.length}</p>
             <button onClick={() => store.dispatch({type: 'add', good: {title: `天貓商品${Date.now()}`, price: 200}})}>添加購物車</button>
         </div>
        )
    }
    
    // 購物車組件
    function Cart() {
        let goods = store.getState().goods
        return (
            <ul>
            {
                goods.map((good, index) => {
                 return (
                     <li key={index}>
                         <span>{good.title}</span>
                         <button onClick={() => store.dispatch({type: 'delete', good: {index}})}>刪除</button>
                     </li>
                 )
             })
         }
         </ul>
     )
    }
    
    // 使用 react-router-dom 配置兩個頁面
    function App() {
      return (
        <div className="App">
          <div>
              <Link to="/tb">淘寶</Link> | 
              <Link to="/tm">天貓</Link> 
          </div>
          
          <Route path="/tb" component={TbItem}></Route>
          <Route path="/tm" component={TmItem}></Route>
          <Cart></Cart>
        </div>
      );
    }
    
    export default App
    
  4. 在 index.js 中訂閱 store

    一旦 store 發(fā)生更改禁灼,則重新 render 整個 App

    /*************index.js***************/
    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './App'
    import store from './store'
    import {BrowserRouter} from 'react-router-dom'
    
    const render = () => {
        ReactDOM.render(
            <BrowserRouter>
                <App />
            </BrowserRouter>, 
            document.getElementById('root')
        )
    }
    // 第一次啟動時管挟,直接渲染
    render()
    // 監(jiān)聽 store 的變化
    store.subscribe(render)
    

二. react-redux 及中間件使用

從上面的例子中可以看到,使用原生的 redux 對 store 的讀取和操作都比較麻煩弄捕,同時還需要在 index.js 上手動調(diào)用渲染函數(shù)僻孝。因此,在實際項目中守谓,通常使用 react-redux 庫結(jié)合 redux-thunk 中間件(用于異步操作)來管理狀態(tài)穿铆,同樣以上述中的例子,看看 react-redux 的實現(xiàn)有什么不同斋荞。

  1. 安裝

    npm install react-redux redux-thunk --save

  2. 同樣需要創(chuàng)建一個倉庫來存儲

    一般將 reducer 獨立為一個文件書寫荞雏,使代碼結(jié)構(gòu)更加清晰

    /************cart.redux.js************/
    // cartReducer 與上一章節(jié)保持一致
    const cartReducer = (state = {
        goods: []
    }, action) => {
        // ...
    }
    // 定義 action 操作
    const addGood = (good) => ({type: 'add', good})
    const deleteGood = (good) => ({type: 'delete', good}) 
    // 異步 action,使用 thunk 中間件之后可以如下書寫
    const asyncAddd = (good) => dispatch => {
        setTimeout(() => {
            dispatch({type: 'add', good})
        }, 2000)
    }
    // 將 reducer 和 action 方法導(dǎo)出
    export {cartReducer, addGood, deleteGood, asyncAddd}
    
  3. 使用 Provider 包裹 App,并傳遞 store 參數(shù)
    /****************index.js******************/
    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './App'
    import {BrowserRouter} from 'react-router-dom'
    
    import {Provider} from 'react-redux'
    import {createStore, applyMiddleware} from 'redux'
    import thunk from 'redux-thunk'
    import {counterReducer} from './cart.redux'
    const store = createStore(counterReducer, applyMiddleware(thunk))
    
    ReactDom.render(
        <BrowserRouter>
            <Provider store={store}>
                <App />
            </Provider>
        </BrowserRouter>,
        document.querySelector('#root')
    )
    
  4. 使用 connect 高階組件加工 App 組件
    /****************app.js******************/
    // ...
    import {connect} from 'react-redux'
    import {addGood, deleteGood, asyncAdd} from './reactRedux/cart.redux'
    
    function App() {
        // ...
    }
    App = connect(
      state => ({goods: state.goods}), 
      {addGood, deleteGood, asyncAdd}
    )(App)
    export default App
    

    也可以配置裝飾器模式凤优,采用裝飾器模式書寫:

    @connect(
       state => ({goods: state.goods}), 
      {addGood, deleteGood, asyncAdd}
    )
    // 使用裝飾器時需要用 class 聲明組件
    class App extends React.Component {
        // ...
    }
    

    在 App 組件內(nèi)需要讀取商品列表可采用 this.props.goods 獲取悦陋,需要添加商品則可以調(diào)用 this.props.addGood(good),需要刪除商品時則可以調(diào)用 this.props.deleteGood(good)筑辨。至于如何將參數(shù)傳遞給子組件 TbItemTmItem 則有三種實現(xiàn)方案:

    • 使用父子組件通信的方法俺驶,通過 <Provider></Provider> <Consumer></Consumer> 實現(xiàn);

    • 同樣用 connect 高階組件裝飾一下組件棍辕,組件內(nèi)通過 this.props 獲取 store 中的屬性和方法暮现;

    • 利用 context 實現(xiàn),在 Parent 組件中定義 靜態(tài)屬性childContextTypesgetChildContext 方法楚昭,Child 組件中定義 靜態(tài)屬性 contextTypes 并在構(gòu)造函數(shù)中將 context 傳遞給 super()函數(shù)

      /****************父組件******************/
      // ...
      import PropTypes from 'prop-types'
      class App extends React.Component {
          static childContextTypes = {
              goods: PropTypes.object
          }
          getChildContext() {
              return { goods: this.props.goods };
          }
         // ...
      }
      
      /****************子組件******************/
      class TbItem extends React.Component {
          static contextTypes = {
              goods: PropTypes.object,
          }
          constructor(props, context){
              super(props, context)
              // ...
          }
         // ...
      }
      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栖袋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哪替,更是在濱河造成了極大的恐慌栋荸,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凭舶,死亡現(xiàn)場離奇詭異晌块,居然都是意外死亡,警方通過查閱死者的電腦和手機帅霜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門匆背,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人身冀,你說我怎么就攤上這事钝尸。” “怎么了搂根?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵珍促,是天一觀的道長。 經(jīng)常有香客問我剩愧,道長猪叙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任仁卷,我火速辦了婚禮穴翩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锦积。我一直安慰自己芒帕,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般募判。 火紅的嫁衣襯著肌膚如雪磺箕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天潭千,我揣著相機與錄音,去河邊找鬼。 笑死本砰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钢悲。 我是一名探鬼主播点额,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼莺琳!你這毒婦竟也來了还棱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惭等,失蹤者是張志新(化名)和其女友劉穎珍手,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辞做,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡琳要,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了秤茅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稚补。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖框喳,靈堂內(nèi)的尸體忽然破棺而出课幕,到底是詐尸還是另有隱情,我是刑警寧澤五垮,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布乍惊,位于F島的核電站,受9級特大地震影響放仗,放射性物質(zhì)發(fā)生泄漏润绎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一匙监、第九天 我趴在偏房一處隱蔽的房頂上張望凡橱。 院中可真熱鬧,春花似錦亭姥、人聲如沸稼钩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坝撑。三九已至静秆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巡李,已是汗流浹背抚笔。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侨拦,地道東北人殊橙。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像狱从,于是被迫代替她去往敵國和親膨蛮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容