React学习笔记4-深入Redux

这篇六个点:1 Redux简介 2 中间件 3 异步流 4 Redux与路由 5 Redux与组件 6 应用实例。

4.1 Redux简介

Redux核心运行流畅
4.1.1 Redux三大原则

1 单一数据源
在Redux思想里,一个应用只有唯一的数据源,好处是整个应用状态都保存在一个对象中,可以随时取出整个应用的状态进行持久化,这样的设计页尾服务端渲染提供了可能。
2 状态是只读的
在Redux中,并不会自己定义一个store,而是定义一个reducer,reducer根据当前触发的action对当前应用的状态进行更新
3 状态修改均由纯函数完成
在redux里,通过定义reducer来确定状态的修改,每一个reducer都是纯函数,接受一定的输入,必定得到一定的输出。

4.1.2 Redux四个部分

整体结构

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);
        // 状态改变就执行
        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,可以创建一个store对象,store又包含4个方法。
getState():获取store中当前的状态。
dispatch(action):分发一个action并返回这个action,这是唯一能改变store中数据的方式。
subscribe():注册一个监听者,当store发生变化时会被调用。

  1. action
    唯一可以改变状态就是触发action,它是一个普通对象,用于描述已经发生的事件。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发起action之后,最终是通过reducer指定如何state,reducer是用来修改状态的,要注意的是不要在reducer中直接修改state,推荐创建一个新的移入,重新通过getState返回新的状态,最终通过subscribe重新渲染。
  2. connect

4.2 中间件

https://www.jianshu.com/p/ae7b5a2f78ae

11.png

这是redux的数据流流程,当增加middleware后,就可以在途中对action进行截获。
。且由于业务场景的多样性,单纯的修改 dispatch 和 reduce 人显然不能满足大家的需要,因此对 redux middleware 的设计是可以自由组合,自由插拔的插件机制。也正是由于这个机制,我们在使用 middleware 时,我们可以通过串联不同的 middleware 来满足日常的开发,每一个 middleware 都可以处理一个相对独立的业务需求且相互串联:
22.png

如上图所示,派发给 redux Store 的 action 对象,会被 Store 上的多个中间件依次处理,如果把 action 和当前的 state 交给 reducer 处理的过程看做默认存在的中间件,那么其实所有的对 action 的处理都可以有中间件组成的。值得注意的是这些中间件会按照指定的顺序一次处理传入的 action,只有排在前面的中间件完成任务之后,后面的中间件才有机会继续处理 action,同样的,每个中间件都有自己的“熔断”处理,当它认为这个 action 不需要后面的中间件进行处理时,后面的中间件也就不能再对这个 action 进行处理了。

而不同的中间件之所以可以组合使用,是因为 Redux 要求所有的中间件必须提供统一的接口,每个中间件的尉氏县逻辑虽然不一样,但只要遵循统一的接口就能和redux以及其他的中间件对话了。

4.3 Redux异步流

  • 1 redux-thunk中间件
    redux-thunk是用于在redux中处理异步action的中间件,它 统一了异步和同步action的调用方式,使得异步过程放在action级别解决,避免异步操作对component的耦合。
    使用这个插件,可以让action创建函数先不返回一个action对象,而是返回一个函数,函数传递两个参数(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);
        // 状态改变就执行
        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';
//要渲染组件内的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

https://www.jianshu.com/p/6a45e2dfc9d9/

文件结构

app.js:整个应用的入口
views文件夹:页面的入口文件,常在路由中引用
components文件夹:下面对应页面的文件夹,里面一堆组件
containers :配置文件
layouts:公用的组件和样式。如菜单,header等
redux:Redux store相关的配置
routes:路由相关的配置

-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" href="https://reactjs.org"
          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;

继续修改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" href="https://reactjs.org"
          target="_blank" rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
        {routes}
    </div>
  );
}
export default App;

成功实现路由

准备首页的数据
P242 后面看不懂 略

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343