React开发管理后台实践2---React基本内容学习

在这一节里,我们以一个医生管理自己的患者为例,向大家介绍React基本内容。包括React、Redux和Router,以及基于Ajax的前后端联调功能。

页面组件显示

假设我们页面显示如下内容,标题是“患者管理”,第一个列表标题为“高血压患者”,然后是高血压患者列表,第二个列表标题为“糖尿病患者”,然后是糖尿病患者列表。
在程序入口点src/App.js中添加如下代码:

import React, { Component } from 'react';
import HighBlood from './HighBlood';
import Diabete from './Diabete';
const diabetes = [
    {'patientId': 201, 'patientName': 'D201'},
    {'patientId': 202, 'patientName': 'D202'},
    {'patientId': 203, 'patientName': 'D203'},
    {'patientId': 204, 'patientName': 'D204'}
];
class App extends Component {
    render() {
        const title = '患者管理';
        return (
            <div>
                <h1>{title}</h1>
                <HighBlood></HighBlood>
                <Diabete patients={diabetes}></Diabete>
            </div>
        );
    }
}
//......

其中HighBlood和Diabete是两个子组件,分别定义在src/HighBlood.js和src/Diabete.js中。我们将糖尿病患者,我们使用全局变量数组diabetes来存储,并通过patients属性传递给Diabete子组件。而高血压患者列表数据将定义在HighBlood组件内部的state中。
我们首先来看高血压组件src/HighBlood.js:

import React, { Component } from 'react';

class HighBlood extends Component {
    constructor(props) {
        super(props);
        this.state = {
            ps: [
                {'patientId': 101, 'patientName': 'p101'},
                {'patientId': 102, 'patientName': 'p102'},
                {'patientId': 103, 'patientName': 'p103'},
                {'patientId': 104, 'patientName': 'p104'}
            ]
        };
    }

    addPatient = () => {
        console.log('添加高血压患者');
        this.setState({
            ps: [...this.state.ps, {'patientId': 105, 'patientName': 'p105!'}
            ]
        });
    }

    render() {
        const content = '高血压患者';
        return (
            <div>
                <h2>{content}</h2>
                <button onClick={this.addPatient}>添加患者</button>
                <ul>
                    {this.state.ps.map(v => {
                        return <li key={v.patientId}>{v.patientName}</li>
                    })}
                </ul>
            </div>
        );
    }
}

export default HighBlood;

我们通过构造函数中定义state中具有一个ps属性,是一个对象数组,保存患者列表数据。与糖尿病患者组件不同,采用state定义的属性可以动态更加,也就是界面中的“添加患者”按钮是可以使用的。
接着我们定义了一个“添加患者”按钮,并定义单击响应函数为addPatient,注意这里addPatient函数定义时只能采用箭头函数形式,否则会报找不到this的错误(当然也可以有其他方法来避免这个错误,大家只需要掌握这种就可以了)。
接下来我们来看render函数,其首先显示了列表标题。然后定义ul元素,state中的数组ps调用map函数,在其内部用箭头函数定义了对数组每个元素的操作。这里是利用数组元素的值创建一个li元素。
如果我们点击添加按钮,就会执行addPatient函数,在这个函数中,我们通过this.setState来重新给ps数组赋值,在赋值时我们先用展开运算符,将原数组内容展开为列表,然后再添加新元素。
接下来我们来看Diabete组件,这个组件与HighBlood的唯一区别就是患者列表数据是通过父组件以属性方式传递过来的。这里需要注意的一点,由于患者列表是父组件通过属性传递过来的,因此患者列表是不能改变的,所以这里的添加按钮是不起作用的,代码如下所示:

import React, { Component } from 'react';

class Diabete extends Component {
    addPatient = () => {
        console.log('添加糖尿病患者');
    }

    componentWillMount() {
        console.log('生命周期函数:componentWillMount');
    }

    componentDidMount() {
        console.log('生命周期函数:componentDidMount ^_^');
    }

    render() {
        const content = '糖尿病患者';
        return (
            <div>
                <h2>{content}</h2>
                <button onClick={this.addPatient}>添加患者</button>
                <ul>
                    {this.props.patients.map(v => {
                        return <li key={v.patientId}>{v.patientName}</li>;
                    })}
                </ul>
            </div>
        );
    }
}

export default Diabete;

Redux初步

利用Redux管理患教文章数量

首先安装redux为:

npm install redux --save

我们首先定义事件类型,在患教文章管理中,我们有两个事件,一个是添加患教文章,一个删除患教文章,我们在src/action_type.js定义这两个事件:

export const ADD_ARTICLE = 'AddArticle'
export const REMOVE_ARTICLE = 'RemoveArticle'

接着我们定义产生这两个事件的方法,在src/action.js文件中定义:

import { ADD_ARTICLE, REMOVE_ARTICLE } from './action_type'

export function createAddArticleAction() {
    let action = { type: ADD_ARTICLE }
    return action
}

export function createRemoveArticleAction() {
    let action = { type: REMOVE_ARTICLE }
    return action
}

接着我们定义reducer来处理这两个事件,如src/reducer.js文件所示:

// Reducer定义
import { ADD_ARTICLE, REMOVE_ARTICLE } from './action_type'

export function articleReducer(state={}, action) {
    console.log(`articleReducer type=${action.type}...`)
    switch (action.type) {
        case ADD_ARTICLE:
            if (!state.articleNum) {
                state.articleNum = 0
            }
            state.articleNum += 1
            console.log(`articleReducer articleNum=${state.articleNum}...`)
            return state
        case REMOVE_ARTICLE:
            if (!state.articleNum) {
                state.articleNum = 0
            }
            state.articleNum -= 1
            return state
        default:
            return state
    }
}

我们首先在src/index.js文件中引入redux的store对象,并进行监听:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux'
import './index.css'
import App from './App'
import registerServiceWorker from './registerServiceWorker'
import { articleReducer } from './reducer'

const store = createStore(articleReducer)
function render() {
    ReactDOM.render(<App store={store} />, document.getElementById('root'))
}
render()
registerServiceWorker()
store.subscribe(render)

最后我们在页面中添加增加文章和删除文章按钮,并添加相应的事件处理函数,如下所示:

import React, { Component } from 'react'
import HighBlood from './HighBlood'
import Diabete from './Diabete'
import { createAddArticleAction, createRemoveArticleAction } from './action'

const diabetes = [
    {'patientId': 201, 'patientName': 'D201'},
    {'patientId': 202, 'patientName': 'D202'},
    {'patientId': 203, 'patientName': 'D203'},
    {'patientId': 204, 'patientName': 'D204'}
]

class App extends Component {
    addArticle = () => {
        console.log('App.addArticle')
        const store = this.props.store
        store.dispatch(createAddArticleAction())
    }
    removeArticle = () => {
        console.log('App.removeArticle')
        const store = this.props.store
        store.dispatch(createRemoveArticleAction())
    }

    render() {
        const title = '患者管理'
        const store = this.props.store.getState()       
        let num = 0
        if (store.articleNum) {
            num = store.articleNum
        }
        return (
            <div>
                <h1>{title}</h1>
                <span>文章数:{num}</span><br />
                <button onClick={this.addArticle}>添加文章</button><br />
                <button onClick={this.removeArticle}>删除文章</button>
                <HighBlood></HighBlood>
                <Diabete patients={diabetes}></Diabete>
            </div>
        )
    }
}

export default App

由上面的代码可以看到,在render方法中,我们通过属性获取store中的state,可以得到文章数量并显示在界面中。在添加或删除文章时,我们生成相应的事件,通过store.dispatch方法,最终调用reducer中相应的处理函数来进行处理。
为了使程序更加优雅,我们可以将事件生成函数引用移出App组件。我们在index.js中引入事件,并以属性的形式传递给App组件,如下所示:

......

import { articleReducer } from './reducer'
import { createAddArticleAction, createRemoveArticleAction } from './action'

const store = createStore(articleReducer)
function render() {
    ReactDOM.render(<App store={store} createAddArticleAction={createAddArticleAction} createRemoveArticleAction={createRemoveArticleAction} />, document.getElementById('root'))
}
......

我们在App组件中,可以通过属性来使用这些函数,如下所示:

......
class App extends Component {
    addArticle = () => {
        console.log('App.addArticle')
        const store = this.props.store
        const createAddArticleAction = this.props.createAddArticleAction
        store.dispatch(createAddArticleAction())
    }
    removeArticle = () => {
        console.log('App.removeArticle')
        const store = this.props.store
        const createRemoveArticleAction = this.props.createRemoveArticleAction
        store.dispatch(createRemoveArticleAction())
    }

    render() {
......

Redux处理异步任务

我们首先来安装一个三方库:

npm install redux-thunk --save

开启异步中间件,在index.js文件中引入thunk和中间件:

......
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import './index.css'
import App from './App'
......

const store = createStore(articleReducer, applyMiddleware(thunk))
function render() {
    ReactDOM.render(<App store={store} createAddArticleAction={createAddArticleAction} createRemoveArticleAction={createRemoveArticleAction} createAddArticleAsyncAction={createAddArticleAsyncAction} />, document.getElementById('root'))
}
......

我们在第3行引入redux的applyMiddleware,在第4行引入异步处理中间件thunk,在创建store时加入中间件初始化,并将异步事件createAddArticleAsyncAction以属性形式传递给App组件。
在App组件中,我们添加一个异步添加按钮,如下所示:

    addArticleAsync = () => {
        console.log('App.addArticleAsync...........')
        const store = this.props.store
        const createAddArticleAsyncAction = this.props.createAddArticleAsyncAction
        store.dispatch(createAddArticleAsyncAction())
    }

其实这部分代码与同步事件基本没有什么区别。
在src/action.js文件中添加异步事件,如下所示:

export function createAddArticleAsyncAction() {
    return dispatch => {
        setTimeout( () => { dispatch(createAddArticleAction()) }, 2000)
    }
}

在异步处理函数中,我们返回的是一个箭头函数,参数为dispatch,其功能就是调用延时函数,在2秒后调用dispatch函数,执行真正的添加操作。

Redux开发调试工具

为开发Redux应用方便,可以安装redux的调试工具,打开Chrome的“更多工具”=》“扩展程序”,点击左上角菜单,点击最下面“打开Chrome网上应用商店”,在其中搜索Redux DevTools,安装该插件。
激活Redux DevTools需要在src/index.js文件中添加如下代码:

......
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
......
const store = createStore(articleReducer, 
    compose(
        applyMiddleware(thunk),
        window.devToolsExtension ? window.devToolsExtension() : f=>f
    )
)
......

首先引入redux中的compose函数,接着在创建store时使用compose函数激活开发调试工具。

使用react-redux

直接使用redux还是相对比较烦琐,我们可以使用react-redux来简化这一过程,通过如下命令安装:

npm install react-redux --save

我们在src/index.js文件中引入react-redux的Provider组件,代码如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import './index.css'
import App from './App'
import registerServiceWorker from './registerServiceWorker'
import { articleReducer } from './reducer'
import { createAddArticleAction, createRemoveArticleAction, createAddArticleAsyncAction } from './action'

const store = createStore(articleReducer, 
    compose(
        applyMiddleware(thunk),
        window.devToolsExtension ? window.devToolsExtension() : f=>f
    )
)

ReactDOM.render(
        (
            <Provider store={store}>
                <App />
            </Provider>
        ), 
        document.getElementById('root'))
registerServiceWorker()

这里最大的变化是我们不需要Render函数和监听了,同时store只需要作为属性传递给Provider组件即可。
接下来我们需要改造App组件,将src/App.js改为如下形式:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createAddArticleAction, createRemoveArticleAction, createAddArticleAsyncAction } from './action'
import HighBlood from './HighBlood'
import Diabete from './Diabete'

const diabetes = [
    {'patientId': 201, 'patientName': 'D201'},
    {'patientId': 202, 'patientName': 'D202'},
    {'patientId': 203, 'patientName': 'D203'},
    {'patientId': 204, 'patientName': 'D204'}
]

class App extends Component {
    render() {
        const title = '患者管理'
        const store = this.props.store
        const state = store.getState()
        return (
            <div>
                <h1>{title}</h1>
                <span>文章数:{this.props.articleNum}</span><br />
                <button onClick={this.props.createAddArticleAction}>添加文章</button><br />
                <button onClick={this.props.createRemoveArticleAction}>删除文章</button>
                <button onClick={this.props.createAddArticleAsyncAction}>异步添加</button>
                <HighBlood></HighBlood>
                <Diabete patients={diabetes}></Diabete>
            </div>
        )
    }
}

const mapStatetoProps = (state) => {
    return { articleNum: state.articleNum }
}
const actionCreators = { createAddArticleAction, createRemoveArticleAction, createAddArticleAsyncAction }
App = connect(mapStatetoProps, actionCreators)(App)

export default App

大家可以看到,我们在App组件中,就不需要使用dispatch了,直接使用属性中对应的方法即可。在最后面,我们用mapStatetoProps这时状态中变量对应于本组件中的属性,actionCreators定义事件生成函数为组件属性。
接下来我们安装babel插件:

npm install babel-plugin-transform-decorators-legacy --save

然后在package.json文件的babel定义部分添加插件,如下所示:

  "babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
        "transform-decorators-legacy"
    ]
  },

如上所示,加入了transform-decorators-legacy插件。
接着我们就可以简化App组件中的代码了,如下所示:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createAddArticleAction, createRemoveArticleAction, createAddArticleAsyncAction } from './action'
import HighBlood from './HighBlood'
import Diabete from './Diabete'

const diabetes = [
    {'patientId': 201, 'patientName': 'D201'},
    {'patientId': 202, 'patientName': 'D202'},
    {'patientId': 203, 'patientName': 'D203'},
    {'patientId': 204, 'patientName': 'D204'}
]

@connect(
    (state) => {
        return { articleNum: state.articleNum, state: state }
    },
    { createAddArticleAction, createRemoveArticleAction, createAddArticleAsyncAction }
)
class App extends Component {
    render() {
        const title = '患者管理 v0.0.2'
        console.log(`num=${this.props.articleNum}`)
        return (
            <div>
                <h1>{title}</h1>
                <span>文章数:{this.props.articleNum} vs {this.props.state.articleNum}</span><br />
                <button onClick={this.props.createAddArticleAction}>添加文章</button><br />
                <button onClick={this.props.createRemoveArticleAction}>删除文章</button>
                <button onClick={this.props.createAddArticleAsyncAction}>异步添加</button>
                <HighBlood></HighBlood>
                <Diabete patients={diabetes}></Diabete>
            </div>
        )
    }
}

export default App

通过使用@connect语法,可以精简react-redux绑定代码,该函数有两个参数,第一个参数是属性中需要state的属性,第二个参数是需要自动dispatch的事件。

使用Router

Router 4是最新版本的路由组件,可以在Web、ReactNative上统一使用。首先是先安装库:

npm install react-router-dom --save

我们首先在src/index.js文件中定义Router,如下所示:

......
import { Provider } from 'react-redux'
import { BrowserRouter, Route, Link } from 'react-router-dom'
import './index.css'
......
ReactDOM.render(
    (
        <Provider store={store}>
            <BrowserRouter>
                <div>
                    <ul>
                        <li><Link to='/'>患者列表</Link></li>
                        <li><Link to='/patientDetail'>患者详情</Link></li>
                        <li><Link to='/articleList'>文章列表</Link></li>
                    </ul>
                    <Route path='/' exact component={App}></Route>
                    <Route path='/patientDetail' component={PatientDetail}></Route>
                    <Route path='/articleList' component={ArticleList}></Route>
                </div>
            </BrowserRouter>
        </Provider>
    ), 
    document.getElementById('root')
)
......

在第2行引入router中常用组件。在页面上方定义三个导航菜单,分别对应三个页面。通过不同的URL显示不同的组件。
然后再分别定义PatientDetail组件:

import React, { Component } from 'react';

class PatientDetail extends Component {
    render() {
        return <h1>患者详情</h1>
    }
}

export default PatientDetail

再定义文章列表页面:

import React, { Component } from 'react';

class ArticleList extends Component {
    render() {
        return <h1>文章列表</h1>
    }
}

export default ArticleList

这样就组成了一个最简单的多页面应用。由此可见Router功能的强大。在后面我们要用到的开源后台应用,就是采用这一基本原理来实现的。

迷你完整应用

我们要实现的功能是用户在访问任意一个页面时,如果没有登录userId<=0,则跳转到登录页面,如果已经登录,显示用户名和注销按钮,点击注销按钮回到登录页面。在登录页面点击登录按钮,进入患者列表页面,如果访问不存在的页面,显示404页面。我们还有一个关于我们页面,用户不登录也可以看。
为了开发这一应用,我们现在有两个主要的功能,一个是登录相关逻辑,另一个是患者列表页面逻辑。为此我们分别设计Auth.redux.js和Home.redux.js两个reducer。因为在我们的体系架构下,只能有一个reducer,所以我们需要使用combineReducers组件。我们新建src/reducers.js文件,内容如下所示:

// 合并所有reducers
import { combineReducers } from 'redux'
import { authReducer } from './Auth.redux'
import { articleReducer } from './Home.redux'

export default combineReducers({
    authReducer,
    articleReducer
})

我们在程序入口处的src/index.js中,生成全局store时,引用这个合并后的reducers,代码如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
import './index.css'
import registerServiceWorker from './registerServiceWorker'
import reducers from './reducers'
import App from './App'
import Login from './Login'
import NotFound from './NotFound'

const store = createStore(reducers, 
    compose(
        applyMiddleware(thunk),
        window.devToolsExtension ? window.devToolsExtension() : f=>f
    )
)


ReactDOM.render(
    (
        <Provider store={store}>
            <BrowserRouter>
                <Switch>
                    <Route path='/login' component={Login}></Route>
                    <Route path='/home' component={App}></Route>
                    <Route path='/not-found' component={NotFound}></Route>
                    <Redirect to='/not-found'></Redirect>
                </Switch>
            </BrowserRouter>
        </Provider>
    ), 
    document.getElementById('root')
)
registerServiceWorker()

在上面的代码中,我们在createStore时传入的是我们刚刚创建的统一的reducer,其中包括articleReducer和authReducer两个元素。接下来路由方面,我们定义了登录页Login,首页为App组件,404页面,并定义如果没有任何路由命中,则直接转到404页面。
我们先来看登录页面src/Login.js定义:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import { login } from './Auth.redux'

@connect(
    (state) => {
        return {
            userId: state.authReducer.userId
        }
    },
    { login }
)
class Login extends Component {
    constructor(props) {
        super(props)
    }

    render() {
        const redirectToPage = <Redirect to='/home'></Redirect>
        const page = (
            <div>
                <h2>请登录系统</h2>
                <button onClick={this.props.login}>登录</button>
            </div>
        )
        return this.props.userId > 0 ? redirectToPage : page
    }
}

export default Login

我们首先将state.authReducer.userId取过来作为属性,页面内容有两种可能性,如果userId<=0则显示登录页面,userId>0则显示首页(患者列表页面)。其中的登录事件定义在src/Auth.redux.js中,如下所示:

// 用于登录、注销、注册
// 定义事件类型
export const LOGIN = 'login'
export const LOGOUT = 'logout'

// 定义reducer
export function authReducer(state={userId: 0, userName: '游客'}, action) {
    switch (action.type) {
        case LOGIN:
            return { ...state, userId: 100, userName: '王一' }
        case LOGOUT:
            return  { ...state, userId: 0, userName: '游客' }
        default:
            return state
    }
}

// 定义事件生成函数
export function login() {
    let action = { type: LOGIN }
    return action
}

export function logout() {
    let action = { type: LOGOUT }
    return action
}

接下来我们来看由App组件定义的导航页面src/App.js,如下所示:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Route, Link } from 'react-router-dom'
import { logout } from './Auth.redux'
import Home from './Home'
import PatientDetail from './PatientDetail'
import ArticleList from './ArticleList'

@connect(
    (state) => {
        return {
            userId: state.authReducer.userId,
            userName: state.authReducer.userName,
        }
    },
    { logout }
)
class App extends Component {
    constructor(props) {
        super(props)
    }

    render() {
        return (
            <div>
                <h2>用户:{this.props.userName}</h2>
                <button onClick={this.props.logout}>注销</button>
                <ul>
                    <li><Link to='/home'>患者列表</Link></li>
                    <li><Link to='/home/patientDetail'>患者详情</Link></li>
                    <li><Link to='/home/articleList'>文章列表</Link></li>
                </ul>
                <Route path='/home' exact component={Home}></Route>
                <Route path='/home/patientDetail' component={PatientDetail}></Route>
                <Route path='/home/articleList' component={ArticleList}></Route>
            </div>
        )
    }
}

export default App

我们在页面首部显示用户名,然后是注销按钮,然后就是导航链接。我们在这里假设Home组件代表的患者列表页面,需要用户登录后才能看,其他页面则不需要。
我们接下来看患者列表页面src/Home.js,如下所示:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Route, Link, Redirect } from 'react-router-dom'
import { articleReducer, addArticle, removeArticle, addArticleAsync } from './Home.redux'
import HighBlood from './HighBlood'
import Diabete from './Diabete'
import PatientDetail from './PatientDetail'
import ArticleList from './ArticleList'

const diabetes = [
    {'patientId': 201, 'patientName': 'D201'},
    {'patientId': 202, 'patientName': 'D202'},
    {'patientId': 203, 'patientName': 'D203'},
    {'patientId': 204, 'patientName': 'D204'}
]

@connect(
    (state) => {
        return { 
            articleNum: state.articleReducer.articleNum,
            userId: state.authReducer.userId
        }
    },
    { addArticle, removeArticle, addArticleAsync }
)
class Home extends Component {
    constructor(props) {
        super(props)
    }

    render() {
        const title = '患者管理 v0.0.2'
        const redirectToLogin = <Redirect to='/login'></Redirect>
        const page = (
            <div>
                <h1>{title}</h1>
                <span>文章数:{this.props.articleNum}</span><br />
                <button onClick={this.props.addArticle}>添加文章</button><br />
                <button onClick={this.props.removeArticle}>删除文章</button>
                <button onClick={this.props.addArticleAsync}>异步添加</button>
                <HighBlood></HighBlood>
                <Diabete patients={diabetes}></Diabete>
            </div>
        )
        return this.props.userId<=0 ? redirectToLogin : page
    }
}

export default Home

这里也将state.authReducer.userId取出来作为属性,也分为两种情况,一种是userId>0,此时显示患者列表页面,另一种是userId<=0,则跳转到登录页面。
Home组件所对应的Reducer为src/Home.redux.js,如下所示:

// 定义事件类型
export const ADD_ARTICLE = 'AddArticle'
export const REMOVE_ARTICLE = 'RemoveArticle'

// 定义reducer
export function articleReducer(state={}, action) {
    if (!state.articleNum) {
        state.articleNum = 0
    }
    switch (action.type) {
        case ADD_ARTICLE:
            return { ...state, articleNum: state.articleNum+1 }
        case REMOVE_ARTICLE:
            return  { ...state, articleNum: state.articleNum-1 }
        default:
            return state
    }
}

// 定义事件生成函数
export function addArticle() {
    let action = { type: ADD_ARTICLE }
    return action
}

export function removeArticle() {
    let action = { type: REMOVE_ARTICLE }
    return action
}

export function addArticleAsync() {
    return dispatch => {
        setTimeout( () => { dispatch(addArticle()) }, 2000)
    }
}

登录功能前后端联调

我们首先安装Ajax支持库:

npm install axios --save

因为我们的开发服务器监听在3000端口,我们服务器的端口为8090,所以直接使用axios发送Ajax请求会出现跨域问题,我们需要在package.json配置代理,将所有请求重定向到8090端口上去,如下所示:

......
  "eslintConfig": {
    "extends": "react-app"
  },
  "proxy": "http://localhost:8443"
}

我们来看登录相关功能,在src/Auth.redux.js文件中,此时login事件就变成向服务器端发送请求,就变为异步事件处理了,如下所示:

// 用于登录、注销、注册
import axios from 'axios'

// 定义事件类型
export const LOGIN = 'login'
export const LOGOUT = 'logout'
export const ON_LOGIN = 'onLoginRst'

const initState = {
    userId: 0,
    userName: '游客',
    roleId: 1,
    roleName: '医生'
}

// 定义reducer
export function authReducer(state=initState, action) {
    switch (action.type) {
        case LOGIN:
            return { ...state, userId: 100, userName: '王一' }
        case LOGOUT:
            return  { ...state, userId: 0, userName: '游客' }
        case ON_LOGIN:
            let data = action.data
            return { ...state, userId: data.userId, userName: data.userName}
        default:
            return state
    }
}

// 定义事件生成函数
export function login() {
    return dispatch => {
        axios.get('/loginReactLearnUserAjax?loginName=1350&loginPwd=123').then(
            res => {
                if (200 == res.status) {
                    dispatch(onLoginRst(res.data))
                }
            }
        )
    }
}
export function onLoginRst(data) {
    return { type: ON_LOGIN, data: data}
}

export function logout() {
    let action = { type: LOGOUT }
    return action
}

我们服务器端使用node.js,处理这个请求首先在wkys/index.js中添加路由:

......
// React测试
var reactHdlr = require('./controller/c_react.js');
......
// React学习测试
handlers['/loginReactLearnUserAjax'] = reactHdlr.loginReactLearnUserAjax;

具体的请求处理函数在controller/c_react.js文件中,如下所示:

/**
*
*/
var url = require("url");
var qs = require("querystring");
var fs = require("fs");
var exec = require("child_process").exec;
var appGlobal = require("../app_global.js");
var baseController = require('../controller/c_base_controller.js');
var db = require("../model/m_mysql.js");

function loginReactLearnUserAjax(request, response) {
    var params = baseController.getRequestParams(request);
    var postParams = request.dataObj;
    var loginName = params.loginName;
    var loginPwd = params.loginPwd;
    var obj = new Object();
    obj.status = 'Ok';
    obj.userId = 201;
    obj.userName = '东方不败';
    obj.roleId = 108;
    obj.roleName = '超级管理员';
    baseController.sendResponseJson(response, obj);
}

// 对外公共接口定义
exports.loginReactLearnUserAjax = loginReactLearnUserAjax;

小结

至此一个小型应用就基本完成了,由此可以看出,写一个完整的程序,这方面的工作量还是比较大的,尤其是我们这个小应用中,还没有考虑页面的布局设计,所以做一个React+Router的应用还是有很高的门槛的。幸好有牛人们给我们写了一些开源框架,使我们可以拿来即用,在下一节中我们将以一个开源React后台管理框架为基础,开发我们的后台管理系统。

猜你喜欢

转载自blog.csdn.net/Yt7589/article/details/82288425
今日推荐