React爬坑之路二:Router+Redux

上一篇写了Antd和Axios的基本操作
之前大标题到五了那么这篇从六开始
ST也是初学小白可能讲的完全不对
大家当做小说随便读读消遣一下就好

React官网:https://reactjs.org/
菜鸟教程:http://www.runoob.com/react/react-tutorial.html
ES6入门: http://es6.ruanyifeng.com/
Ant Design:https://ant.design/index-cn
Redux: https://www.redux.org.cn/
Dva:https://github.com/dvajs/dva

先附上上一篇的链接。React爬坑之路一:React+Antd+Axios
在真正研究Dva前,我们除了先了解一些ES6,React,Antd,Axios的基本知识,还需要知道Router和Redux的工作原理。

六.React-Router配置路由

在实际应用中,我们不可能只有一页页面,切换页面需要通过Router配置,这样输入不同的url就能看到不同的页,或者点击不同的按钮可以在下方加载不同的组件。
这里补充一下组件的概念,其实其本质就是一个类。在页面中我们可以把页面分为导航区,侧边栏,工作区等不同区域,每个区域都是一个组件,组件中还可以继续细分为其他组件。想象在创建页面的时候把不同的组件拿来组合在一起,页头页尾这种公用组件可以始终保持不动,只切换我们想切换的区域。)
一般来讲,我们用React-router-dom来实现,它提供了BrowserRouter, Route, Link等api,让我们可以通过dom事件控制路由。
第一步还是安装。

npm install react-router-dom --save

这时我们新建两个组件aaa.js和bbb.js。

import React, { Component } from 'react';

class AAA extends Component {
    render() {
        return (
            <div>
                <h1>AAA</h1>
            </div>
        )
    }
}
export default AAA;
import React, { Component } from 'react';

class BBB extends Component {
    render() {
        return (
            <div>
                <h1>BBB</h1>
            </div>
        )
    }
}
export default BBB;

并且在首页上搞两个按钮。(不会搞的请去看上一篇)

假装是导航.png

然后,将刚刚的两个组件,和React-router-dom中的一些api导入首页app.js。这里我们用了BrowserRouter,当然也可以用HashRouter,差别就是地址栏有没有#。嗯比如用HashRouter地址栏就是http://localhost:3000/#/。(好像还有别的差别但是我不知道Σ(⊙▽⊙"))

import { BrowserRouter, Route, Link, Switch } from 'react-router-dom';  
import AAA from './aaa';
import BBB from './bbb';

然后在Dom里用Link来改变地址栏的url,注意LinkRoute这些api都需要写在<BrowserRouter>

    <BrowserRouter>  
      <div className="App">
        <Button type="primary"><Link to='/aaa'>假装自己是个导航</Link></Button>
        <Button><Link to='/bbb'>假装自己是另一个</Link></Button>
      </div>
    </BrowserRouter>  

此时点击按钮可以看到地址栏是改变的,会变成http://localhost:3000/aaahttp://localhost:3000/bbb
然后,在下方加上要通过路由切换的组件,当path为aaa或者bbb的时候分别加载AAA和BBB。

    <BrowserRouter>  
      <div className="App">
        <Button type="primary"><Link to='/aaa'>假装自己是个导航</Link></Button>
        <Button><Link to='/bbb'>假装自己是另一个</Link></Button>
        <Switch>
            <Route path="/aaa" exact component={AAA} />
            <Route path="/bbb" exact component={BBB} />
        </Switch>
      </div>
    </BrowserRouter>  

此时,在浏览器中点击两个按钮,应该能看到下方可以切换两种组件啦。

AAA.png
BBB.png

这是最简单的一个路由的实现。在实际的开发中,一般来讲我们会在上方或左侧简历导航栏,通过给导航栏元素添加<Link>来加载页面剩下的内容。听起来有点像iframe?但是路由切换不会像iframe一样直接加载一个页面进来,只是通过路径匹配组件的切换。
当然,实际的开发中我们也不会这么不规范的直接把路由都写在首页里,一般会单写一个router.js专门负责路由的控制。在后面的Dva框架中,我们会看到Dva把路由、公用组件、模型文件、服务文件等在创建工程时就帮我们分类好了,这样整个工程结构就非常清晰。

六.Redux管理状态

在前一篇中我们说了,state是react特有的一个存储数据的地方,我们通过各种操作可以改变state的值,然后更新到页面显示上。
但是,当工程变得非常庞大且复杂的时候,各种各样的state和更新state的方法就会使程序变得很乱。在我们后台开发的时候,一向很讲究逻辑对不对,而前后端分离的情况下,管理前端这些数据,我们也需要进行“有条理的对数据进行操作”。
而进行这个操作的,就是Redux
Redux提供一些api来管理数据,Redux很霸道地告诉我们:数据只能存在我这,并且只能通过我的方式来修改!
它包括三部分:store,action,reducer

  1. store是一个……规范的state,就像一个有条理的数据库,Redux将整个应用的state储存在唯一的store中。
  2. action是……一个有属性的对象,或者说描述了发生了什么的对象,用dispatch(action)来触发,并且这是改变state的唯一方式。
  3. reducer是……具体通过action更新state的那个函数,基本结构是,reducer(state, action) => newstate

简单地讲,state是鸡翅,action是烤熟了的鸡翅,reducer是烤!

(好了,作为一个逻辑思维为0数学从没及过格的人,我们就了解到这吧……什么,还是得实践一下代码吗Σ(⊙▽⊙")我明明是个设计师啊……)


那么我们继续回到上一篇的那个例子上。比如我们有这样一个表格,现在我们想用Redux的方式做一些更改。(用Antd生成列表请看上一篇)

例子.png

首先要用npm或者yarn装上redux。

npm install --save redux

我们大致知道了要创建一个store,并且定义好action和reducer,那么一步一步开始。
一. 创建store
第一步在app.js中,引入redux包中的createStore()方法。

import { createStore } from 'redux';

然后,通过createStore(reducers[,initialState])的方式来创建store,这个方法根据 reducer生成store,并且只能通过此reducer来改变store中的状态,第二个参数是可选的默认初始值。
注意,一个应用中我们只有一个store,存储了全部数据状态,但是会有很多reducer,通过这些reducer合起来创建store需要用到combineReducers方法。
但是我现在只想做一个简单操作,所以用一个reducer做例子。

//初始状态
const initialState = {
    ……
    data: [{
        "key": "1",
        "name": "王大斌",
        "gender": "男"
      },{
        "key": "2",
        "name": "刘小洋",
        "gender": "男"
      }]
}
//创建reducer方法,先原封不动返回state
const myreducer = (state=initialState, action) => {
  return state;
}
//创建store存储区,它只能通过reducer作为参数来构造
const store = createStore(myreducer);

这样store就创好了,可以把store打印到控制台看一下。getState()是store的一个最常见api,用了获取state的值。

console.log("initial state: ", store.getState());
store中的状态.png

Very well,接下来写一个更新state的action。

二. 创建action
Action 是一个带属性的对象,其属性用type来定义,type是必填项,其他的还可以有附带数据,一般写在用payload里。

//创建描述性对象action
const myaction = {
  type: 'ADD_DATA',
  payload: {
        "key": "3",
        "name": "张胖卓",
        "gender": "男"
  }
};

上面已经说过,修改state的唯一方法就是dispatch(action),那么现在我们就把这个action触发一下。

store.dispatch(myaction);

如果是点击一个按钮触发的话,把dispatch放在onClick绑定的函数里就行。

//点击按钮触发
const changeData = () => {
    store.dispatch(myaction);
}

class App extends Component {
    render() {
        return ( 
            <Button type="primary" onClick={changeData}>修改数据</Button>
            ……
        );
    }
}

三. 创建reducer
Store触发了一个action,相当于store宣称:“我要改变自己的state”,此时,需要reducer来执行这个过程。
Reducer的输入参数是当前的state和收到的action,他会返回一个新的 state。注意在reducer中,不能随意更改参数或者加入随机性的各种操作,也就是reducer输入相同的话,输出也必定相同。
我们修改刚刚的reducer如下。

//创建reducer方法
const myreducer = (state=initialState, action) => {
  switch (action.type) {
    case 'ADD_DATA':
        return {
            ...state,
            data:state.data.concat(action.payload)
          }
    default: 
        return state;
  }
}

...state是ES6中的三点运算符,作用是把数组打开进行操作。
可以看到,当action是ADD_DATA的时候,我们把payload里面的内容加在原有的data之后,也就是新加一行数据。
这时,在控制台输出store的值,可以看到已经有所改变。

console.log("change state: ", store.getState());
更新后的store.png

四. 通过监听更新视图
然而,现在视图上却没有刷新,只是store中的值改变了。(这个问题纠结了我两天= =)是因为store跟view层并没有连起来,如果不手动重新render,页面是不会变化的,为此我们需要一个监听函数,监听store中值的变化,当发生变化时重新渲染view。
这个监听由store.subscribe实现。(如果你用了react-redux的话,它里面的connect让我们不需要自己手动去subscribe全局state的变化,它会在内部自动监听并更新。)

class App extends Component {
    listerner() {
      let newState = store.getState();
      this.setState(newState);   
    }
    //保持监听
    componentDidMount () {
        store.subscribe(this.listerner.bind(this));
    }
}

这样,整个流程实现了:

  1. 创建store存储数据,并把初始值绑定到view层。
  2. 点击按钮,触发store.dispatch(myaction)
  3. store收到action后,调用myreducer = (state=initialState, action)改变store中的state。
  4. store更新后,通过store.subscribe监听函数更新view层。

最终的成果如下:

最终效果.png

ヽ(✿゚▽゚)ノ虽然很混乱不过总算似乎是实现了整套流程。

【很重要的补充说明!】

在上面的例子里,我们只用了redux,事实上在React中,我们一般常用react-redux来进行操作。react-redux提供的结构可以让我们不需要自己手动去写dispatch,subscribe这些函数,它在内部通过自身的结构帮我们完成了这些操作。
Dva,(终于说到了Dva!),它已经封装好了react-router,react-redux,redux-saga等中间件,并给出了清晰地工程目录结构。因而在了解react基础知识后,用Dva来构建工程更加快捷简便。
那么,下一篇,我们就来真正用Dva创建一个工程并进行一些简单操作~
请戳React爬坑之路三:Dva

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

推荐阅读更多精彩内容