React學習筆記4-深入Redux

這篇六個點:1 Redux簡介 2 中間件 3 異步流 4 Redux與路由 5 Redux與組件 6 應用實例勋拟。

4.1 Redux簡介

Redux核心運行流暢
4.1.1 Redux三大原則

1 單一數(shù)據(jù)源
在Redux思想里员寇,一個應用只有唯一的數(shù)據(jù)源嫁盲,好處是整個應用狀態(tài)都保存在一個對象中闹蒜,可以隨時取出整個應用的狀態(tài)進行持久化野哭,這樣的設計頁尾服務端渲染提供了可能双吆。
2 狀態(tài)是只讀的
在Redux中眨唬,并不會自己定義一個store,而是定義一個reducer好乐,reducer根據(jù)當前觸發(fā)的action對當前應用的狀態(tài)進行更新
3 狀態(tài)修改均由純函數(shù)完成
在redux里匾竿,通過定義reducer來確定狀態(tài)的修改,每一個reducer都是純函數(shù)蔚万,接受一定的輸入岭妖,必定得到一定的輸出。

4.1.2 Redux四個部分

整體結(jié)構(gòu)

import { createStore} from 'redux';
// reducer部分
const defaultState={
    myValue:[],
    inputValue:"默認值"
};
function reducer(state=defaultState,action) {
    if (action.type==="add"){
        const newState=JSON.parse(JSON.stringify(state));
        newState.myValue.push(newState.inputValue);
        console.log(newState);
        return newState;
    }
    if (action.type==='change'){
        const newState=JSON.parse(JSON.stringify(state));
        newState.inputValue=action.value;
        console.log(newState);
        return newState;

    }
    return state;
}
// store 部分
const store=createStore(
    reducer,
    window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()
);

class ReduxTest extends Component{
    constructor(props){
        super(props);
        this.state=store.getState();
        console.log(this.state);
        this.handleClick=this.handleClick.bind(this);
        this.handleChange=this.handleChange.bind(this);
        // 狀態(tài)改變就執(zhí)行
        store.subscribe(()=>{
            this.setState(store.getState());
        });
    }
    handleClick(){
        const action={
            type:'add'
        };
        store.dispatch(action);
    }
    handleChange(e){
        const action={
            type:'change',
            value:e.target.value
        };
        store.dispatch(action);
    }
    render(){
        return(
            <div>
                <input
                    value={this.state.inputValue||"HAHA"}
                    onChange={this.handleChange}
                />
                <button onClick={this.handleClick}>改變</button>
            </div>
        )
    }
}
  1. store
import { createStore} from 'redux';
const store=createStore(
    reducer,
    window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()
);

Redux最核心的API,可以創(chuàng)建一個store對象昵慌,store又包含4個方法苔巨。
getState():獲取store中當前的狀態(tài)。
dispatch(action):分發(fā)一個action并返回這個action,這是唯一能改變store中數(shù)據(jù)的方式废离。
subscribe():注冊一個監(jiān)聽者侄泽,當store發(fā)生變化時會被調(diào)用。

  1. action
    唯一可以改變狀態(tài)就是觸發(fā)action,它是一個普通對象蜻韭,用于描述已經(jīng)發(fā)生的事件悼尾。type是必須的,其他可以自定義。
handleChange(e){
        const action={
            type:'change',
            value:e.target.value
        };
        store.dispatch(action);
    }
//實際這么用
function addTo(e){
    return{
        type:'change',
        value:e.target.value
    }
}
handleChange(e){
        store.dispatch(addTo(e));
    }
  1. reducer
    通過dispatch發(fā)起action之后肖方,最終是通過reducer指定如何state闺魏,reducer是用來修改狀態(tài)的,要注意的是不要在reducer中直接修改state俯画,推薦創(chuàng)建一個新的移入析桥,重新通過getState返回新的狀態(tài),最終通過subscribe重新渲染艰垂。
  2. connect

4.2 中間件

http://www.reibang.com/p/ae7b5a2f78ae

11.png

這是redux的數(shù)據(jù)流流程泡仗,當增加middleware后,就可以在途中對action進行截獲猜憎。
娩怎。且由于業(yè)務場景的多樣性,單純的修改 dispatch 和 reduce 人顯然不能滿足大家的需要胰柑,因此對 redux middleware 的設計是可以自由組合截亦,自由插拔的插件機制。也正是由于這個機制柬讨,我們在使用 middleware 時崩瓤,我們可以通過串聯(lián)不同的 middleware 來滿足日常的開發(fā),每一個 middleware 都可以處理一個相對獨立的業(yè)務需求且相互串聯(lián):
22.png

如上圖所示踩官,派發(fā)給 redux Store 的 action 對象却桶,會被 Store 上的多個中間件依次處理,如果把 action 和當前的 state 交給 reducer 處理的過程看做默認存在的中間件卖鲤,那么其實所有的對 action 的處理都可以有中間件組成的肾扰。值得注意的是這些中間件會按照指定的順序一次處理傳入的 action畴嘶,只有排在前面的中間件完成任務之后蛋逾,后面的中間件才有機會繼續(xù)處理 action,同樣的窗悯,每個中間件都有自己的“熔斷”處理,當它認為這個 action 不需要后面的中間件進行處理時区匣,后面的中間件也就不能再對這個 action 進行處理了。

而不同的中間件之所以可以組合使用,是因為 Redux 要求所有的中間件必須提供統(tǒng)一的接口亏钩,每個中間件的尉氏縣邏輯雖然不一樣莲绰,但只要遵循統(tǒng)一的接口就能和redux以及其他的中間件對話了。

4.3 Redux異步流

  • 1 redux-thunk中間件
    redux-thunk是用于在redux中處理異步action的中間件姑丑,它 統(tǒng)一了異步和同步action的調(diào)用方式蛤签,使得異步過程放在action級別解決,避免異步操作對component的耦合栅哀。
    使用這個插件震肮,可以讓action創(chuàng)建函數(shù)先不返回一個action對象,而是返回一個函數(shù)留拾,函數(shù)傳遞兩個參數(shù)(dispatch,getState)
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import { createStore,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';

// reducer部分
const defaultState={
    myValue:[],
    inputValue:"默認值",
    num:3
};
function reducer(state=defaultState,action) {
    if (action.type=='addValue'){
        const newState=JSON.parse(JSON.stringify(state));
        newState.num++;
        console.log(newState);
        return newState;
    }
    return state;
}

// store 部分
const store=createStore(
    reducer,
    applyMiddleware(thunk)
);
class Thunk extends Component{
    constructor(props){
        super(props);
        this.state=store.getState();
        // console.log(this.state);
        this.handleClick=this.handleClick.bind(this);
        this.handleChange=this.handleChange.bind(this);
        // 狀態(tài)改變就執(zhí)行
        store.subscribe(()=>{
            this.setState(store.getState());
        });
    }
    addIfOdd(){
        return (dispatch,getState)=>{
            console.log('111');
            if (this.state.num%2===0){
                return false
            }else {
               setTimeout(()=>{
                   dispatch({type:'addValue'})
               },2000)
            }
        }
    }
    handleClick(){
        store.dispatch(this.addIfOdd());
        console.log("222")
    }
    
    handleChange(){}
    
    render(){
        return(
            <div>
                <input
                    value={this.state.num||"HAHA"}
                    onChange={this.handleChange}
                />
                <button onClick={this.handleClick}>改變</button>
            </div>
        )
    }
}
  • Redux-saga 略

4.4 Redux與路由

http://www.ruanyifeng.com/blog/2016/05/react_router.html

import {BrowserRouter,Route} from 'react-router-dom';
//要渲染組件內(nèi)的render
render() {
        return (
            <div>
                <input
                    value={this.state.num || "HAHA"}
                    onChange={this.handleChange}
                />
                <button onClick={this.handleClick}>改變</button>
                <BrowserRouter>
                    <Route path='/' exact render={() => <div>首頁</div>}/>
                    <Route path='/detail' exact render={() => <div>詳情頁</div>}/>
                </BrowserRouter>
            </div>
        )
    }
ReactDOM.render(
    <Thunk/>,
    document.getElementById('root'),
    function () {
        console.log("加載完畢");
    }
);

4.5 Redux與組件

4.5.1 容器型組件
4.5.2 展示型組件
4.5.3 Redux中的組件

4.6 應用實例

mkdir my-blog
cd my-blog
G:\react\my-blog> npm install --save react react-dom redux react-router react-redux react-router-redux whatwg-fetch

http://www.reibang.com/p/6a45e2dfc9d9/

文件結(jié)構(gòu)

app.js:整個應用的入口
views文件夾:頁面的入口文件戳晌,常在路由中引用
components文件夾:下面對應頁面的文件夾,里面一堆組件
containers :配置文件
layouts:公用的組件和樣式痴柔。如菜單沦偎,header等
redux:Redux store相關(guān)的配置
routes:路由相關(guān)的配置

-views/Home.js

import React,{Component} from 'react';
class Home extends Component {
    constructor(props){
        super(props);
    };
    render(){
        return(
            <h1>Home</h1>
        )
    }
}
export default Home;
  • views/Detail.js
import React,{Component} from 'react';
class Detail extends Component {
    constructor(props){
        super(props);
    };
    render(){
        return(
            <h1>Detail</h1>
        )
    }
}
export default Detail;
  • route/index.js
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Home from '../views/Home';
import Detail from '../views/Detail';
const routes=(
    <BrowserRouter>
        <Route path='/' component={Home}/>
        <Route path='/detail' component={Detail}/>
    </BrowserRouter>
);
export default routes;
  • app.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route} from 'react-router-dom';
import routes from './routes';
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a className="App-link" 
          target="_blank" rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
        {routes}
    </div>
  );
}
export default App;
  • index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
serviceWorker.unregister();
  • index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

添加公用的組件
lauouts/Nav.js

import React,{Component} from 'react';
import {BrowserRouter as Router, Link} from 'react-router-dom';
class Nav extends Component {
    constructor(props){
        super(props);
    };
    render(){
        return(
            <nav>
                <Link to='/'>首頁</Link>
            </nav>
        )
    }
}
export default Nav;

lauouts/Frame.js

import React,{Component} from 'react';
import {Link} from 'react-router-dom';
import Nav from './Nav';
class Frame extends Component {
    constructor(props){
        super(props);
    };
    render(){
        return(
           <div>
               <Nav/>
           </div>
        )
    }
}
export default Frame;

修改routes/index.js

import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Home from '../views/Home';
import Detail from '../views/Detail';
import Frame from '../layouts/Frame'
const routes=(
    <BrowserRouter>
        <Frame/>
        <Route path='/' component={Home}/>
        <Route path='/detail' component={Detail}/>
    </BrowserRouter>
);
export default routes;

繼續(xù)修改App.js


import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route} from 'react-router-dom';
import routes from './routes';
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a className="App-link" 
          target="_blank" rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
        {routes}
    </div>
  );
}
export default App;

成功實現(xiàn)路由

準備首頁的數(shù)據(jù)
P242 后面看不懂 略

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咳蔚,隨后出現(xiàn)的幾起案子豪嚎,更是在濱河造成了極大的恐慌,老刑警劉巖谈火,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疙渣,死亡現(xiàn)場離奇詭異,居然都是意外死亡堆巧,警方通過查閱死者的電腦和手機妄荔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谍肤,“玉大人啦租,你說我怎么就攤上這事』拇В” “怎么了篷角?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長系任。 經(jīng)常有香客問我恳蹲,道長,這世上最難降的妖魔是什么俩滥? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任嘉蕾,我火速辦了婚禮,結(jié)果婚禮上霜旧,老公的妹妹穿的比我還像新娘错忱。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布以清。 她就那樣靜靜地躺著儿普,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掷倔。 梳的紋絲不亂的頭發(fā)上眉孩,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天,我揣著相機與錄音勒葱,去河邊找鬼勺像。 笑死,一個胖子當著我的面吹牛错森,可吹牛的內(nèi)容都是我干的吟宦。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼涩维,長吁一口氣:“原來是場噩夢啊……” “哼殃姓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓦阐,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蜗侈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后睡蟋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踏幻,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年戳杀,在試婚紗的時候發(fā)現(xiàn)自己被綠了该面。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡信卡,死狀恐怖隔缀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情傍菇,我是刑警寧澤猾瘸,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站丢习,受9級特大地震影響牵触,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咐低,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一揽思、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渊鞋,春花似錦绰更、人聲如沸瞧挤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至执俩,卻和暖如春徐钠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背役首。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工尝丐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衡奥。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓爹袁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矮固。 傳聞我的和親對象是個殘疾皇子失息,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361