Redux状态管理方法与实例

状态管理是目前构建单页应用中不可或缺的一环,也是值得花时间学习的知识点。React官方推荐我们使用Redux来管理我们的React应用,同时也提供了Redux的文档来供我们学习,中文版地址为http://cn.redux.js.org/index.html

前言

虽然官方文档上说只需几分钟就能上手 Redux,但是我个人认为即便你看个两三天也可能上手不了,因为文档里面的知识点不仅数量较多,而且还艰涩难懂,不结合一些实例来看很难用于实际项目中去。

但是不要担心自己学不会,这不我就给大家带来了这篇干货,也是我学习Redux的心得体验。

如果你对如何构建React单页应用还不了解的可以先移步我的上一篇文章《React 构建单页应用方法与实例》

那么下面我就将介绍如何利用Redux来管理你的React项目了,而这里我主要教你构建的是基于React + Redux + React-Router的方法,这也是官方文档里介绍的比较少但是项目中却必备的知识点。

项目目录

首先,一个基于React + Redux + React-Router的项目目录可以按照我下方的图片来构建:

其中assets目录用于存放项目的静态资源,如css/图片等,src目录则用于存放React的组件资源。

入口文件配置

在webpack的配置项中,我们需要一个或多个入口文件,这里我就不展示关于package.json及webpack.config.js的文件配置,最后我会提供整个项目的下载链接供大家参考。这里我主要介绍下入口文件index.js的配置说明。

import React from ‘react‘  // 引入React
import { render } from ‘react-dom‘ // 引入render方法
import { Provider } from ‘react-redux‘ // 利用Provider可以使我们的 store 能为下面的组件所用
import { Router, browserHistory } from ‘react-router‘ // Browser history 是由 React Router 创建浏览器应用推荐的 history
import { syncHistoryWithStore } from ‘react-router-redux‘ // 利用react-router-redux提供的syncHistoryWithStore我们可以结合store同步导航事件

import finalCreateStore from ‘./src/store/configureStore‘  //引入增强后的store
import DevTools from ‘./src/containers/DevTools‘  // 引入Redux调试工具DevTools
import reducer from ‘./src/reducers‘  // 引入reducers集合
import routes from ‘./src/routes‘   // 引入路由配置

import ‘./assets/css/bootstrap.min.css‘  // 引入样式文件

// 给增强后的store传入reducer
const store = finalCreateStore(reducer)

// 创建一个增强版的history来结合store同步导航事件
const history = syncHistoryWithStore(browserHistory, store)

render(
    {/* 利用Provider包裹页面 */}
    <Provider store={store}>
        <div>
            {/* 渲染根路由 */}
            <Router history={history} routes={routes} />
            {/* 渲染调试组件 */}
            <DevTools />
        </div>
    </Provider>,
    document.getElementById(‘mount‘)
)

在入口文件中我们尽量只需要保留基本的东西,其余的配置代码我们可以放到相应的配置文件中去,比如路由、reducers及store的配置等。这里我都把它们放置到了独立的js中,只在入口文件中通过import引入,这样管理和维护起来会非常方便,但也会相应增加理解的难度,然而一旦上手就会很容易。那么接下来我们再来看下store配置吧。

store配置

import thunk from ‘redux-thunk‘ // redux-thunk 支持 dispatch function,并且可以异步调用它
import createLogger from ‘redux-logger‘ // 利用redux-logger打印日志
import { createStore, applyMiddleware, compose } from ‘redux‘ // 引入redux createStore、中间件及compose
import DevTools from ‘../containers/DevTools‘ // 引入DevTools调试组件

// 调用日志打印方法
const loggerMiddleware = createLogger()

// 创建一个中间件集合
const middleware = [thunk, loggerMiddleware]

// 利用compose增强store,这个 store 与 applyMiddleware 和 redux-devtools 一起使用
const finalCreateStore = compose(
    applyMiddleware(...middleware),
    DevTools.instrument(),
)(createStore)

export default finalCreateStore

这里我们需要了解中间件(Middleware)的概念。middleware 是指可以被嵌入在框架接收请求到产生响应过程之中的代码,你可以在一个项目中使用多个独立的第三方 middleware,如上面的redux-thunk和redux-logger。详细资料请参考官方文档:
http://cn.redux.js.org/docs/advanced/Mid...

路由配置

上面的入口文件配置中我们把路由配置部分单独放到了routes.js的文件中,这里我们就来看下其配置:

import React from ‘react‘ // 引入react
import { Route, IndexRoute } from ‘react-router‘ // 引入react路由
import { App, Home, Foo, Bar, Antd } from ‘./containers‘ // 引入各容器组件

export default (
    <Route path="/" component={App}>
        <IndexRoute component={Home}/>
        <Route path="index" component={Home}/>
        <Route path="foo" component={Foo}/>
        <Route path="bar" component={Bar}/>
        <Route path="antd" component={Antd}/>
    </Route>
)

这里的路由配置和不使用redux时候是一样的,唯一需要了解的是容器组件和展示组件的概念。上面配置文件中的路由加载的组件都可以认为是容器组件。
(1)顾名思义,展示组件包含在容器组件中,只用作页面展示,不会定义数据如何读取如何改变,只通过this.props接受数据和回调函数;
(2)而容器组件中包含各展示组件的数据,即Props,它们为展示组件或其他组件提供数据和方法。
我们应该把它们放在不同的文件夹中,以示区别,如上面“项目目录”中的containers和components文件夹分别存放容器组件和展示组件。具体说明可以参考文章:http://www.jianshu.com/p/6fa2b21f5df3

根组件配置

import React, { Component } from ‘react‘ // 引入React
import { Link } from ‘react-router‘ // 引入Link处理导航跳转

export default class App extends Component {
    render() {
        return(
            <div>
                <nav className="navbar navbar-default">
                    <div className="container-fluid">
                        <div className="navbar-header">
                            <span className="navbar-brand" href="#">
                                <Link to="/">Redux</Link>
                            </span>
                        </div>
                        <ul className="nav navbar-nav">
                            <li>
                                <Link to="/index" activeStyle={{color: ‘#555‘, backgroundColor: ‘#e7e7e7‘}}>计数器</Link>
                            </li>
                            <li>
                                <Link to="/foo" activeStyle={{color: ‘#555‘, backgroundColor: ‘#e7e7e7‘}}>静态数据</Link>
                            </li>
                            <li>
                                <Link to="/bar" activeStyle={{color: ‘#555‘, backgroundColor: ‘#e7e7e7‘}}>动态数据</Link>
                            </li>
                            <li>
                                <Link to="/antd" activeStyle={{color: ‘#555‘, backgroundColor: ‘#e7e7e7‘}}>结合antd</Link>
                            </li>
                        </ul>
                    </div>
                </nav>
                <div className="panel panel-default">
                    <div className="panel-body">
                        { this.props.children }
                    </div>
                </div>
            </div>
        )
    }
}

整个根组件App.js主要渲染了整个应用的导航和可变区域,这其实和Redux没有关系。需要注意的是to中的URL地址需要和routes.js中的path地址名称一致。

写到这里还没有介绍Redux中的Action及Reducer的配置,那么接下来就来介绍下。

Action配置

import { INCREASE, DECREASE, GETSUCCESS, REFRESHDATA } from ‘../constants‘  // 引入action类型名常量
import ‘whatwg-fetch‘  // 可以引入fetch来进行Ajax

// 这里的方法返回一个action对象
export const increase = n => {
    return {
        type: INCREASE,
        amount: n
    }
}

export const decrease = n => {
    return {
        type: DECREASE,
        amount: n
    }
}

export const refreshData = () => {
    return {
        type: REFRESHDATA
    }
}

export const getSuccess = (json) => {
    return {
        type: GETSUCCESS,
        json
    }
}

function fetchPosts() {
    return dispatch => {
        return fetch(‘data.json‘)
            .then((res) => { console.log(res.status); return res.json() })
            .then((data) => {
                dispatch(getSuccess(data))
            })
            .catch((e) => { console.log(e.message) })
        }
}

// 这里的方法返回一个函数进行异步操作
export function fetchPostsIfNeeded() {

    // 注意这个函数也接收了 getState() 方法
    // 它让你选择接下来 dispatch 什么
    return (dispatch, getState) => {
        return dispatch(fetchPosts())
    }
}

上面返回一个action对象的方法叫做“action 创建函数”,它就是生成action的方法,也是store数据的唯一来源。
上面返回一个函数的方法叫做“异步action”,这里使用的是Redux Thunk middleware,要引入redux-thunk这个专门的库才能使用,这样我们就可以实现异步Ajax请求改变状态等功能了。

Reducer配置

// reducers/count.js
import { INCREASE, DECREASE, GETSUCCESS, REFRESHDATA } from ‘../constants‘ // 引入action类型常量名

// 初始化state数据
const initialState = {
    number: 1,
    lists: [
        {text: ‘整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。‘},
        {text: ‘惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。‘},
        {text: ‘为了描述 action 如何改变 state tree ,你需要编写 reducers。‘},
        {text: ‘就是这样,现在你应该明白 Redux 是怎么回事了。‘}
    ],
    data: []
}

// 通过dispatch action进入
export default function update(state = initialState, action) {

    // 根据不同的action type进行state的更新
    switch(action.type) {
        case INCREASE:
            return Object.assign({}, state, { number: state.number + action.amount })
            break
        case DECREASE:
            return Object.assign({}, state, { number: state.number - action.amount })
            break
        case GETSUCCESS:
            return Object.assign({}, state, { data: action.json })
        case REFRESHDATA:
            return Object.assign({}, state, { data: [] })
        default:
            return state
    }
}
// reducers/index.js
import { combineReducers } from ‘redux‘ // 利用combineReducers 合并reducers
import { routerReducer } from ‘react-router-redux‘ // 将routerReducer一起合并管理
import update from ‘./count‘ // 引入update这个reducer

export default combineReducers({
    update,
    routing: routerReducer
})

这里我们主要需要了解如何通过combineReducers来合并reducers,同时在进入reducer方法后我们必须返回一个state的处理结果来更新state状态,否则会报错。还需注意的是在合并reducers的时候,需要加上routerReducer这个由“react-router-redux”提供的reducer来管理路由的状态更新。

容器组件

上文提到了容器组件和展示组件的区别和含义,这里我们需要在容器组件使用connect来搭配Redux来进行状态管理,这是很关键的一步。

import React, { Component, PropTypes } from ‘react‘ // 引入React
import { connect } from ‘react-redux‘ // 引入connect
import List from ‘../components/List‘  // 引入展示组件List

export default class Foo extends Component {
    render() {

        // 通过this.props获取到lists的值
        const { lists } = this.props

        return(
            <div>
                <ul className="list-group">
                    {将值传入展示组件}
                    { lists.map((e, index) =>
                        <List text={e.text} key={index}></List>
                    )}
                </ul>
            </div>
        )
    }
}

// 验证组件中的参数类型
Foo.propTypes = {
    lists: PropTypes.arrayOf(PropTypes.shape({
        text: PropTypes.string.isRequired
    }).isRequired).isRequired
}

// 获取state中的lists值
const getList = state => {
    return {
        lists: state.update.lists
    }
}

// 利用connect将组件与Redux绑定起来
export default connect(getList)(Foo)

在容器组件中我们需要获取state中的初始状态的时候,我们需要使用connect。任何一个从 connect() 包装好的组件都可以得到一个 dispatch 方法作为组件的 props,以及得到全局 state 中所需的任何内容。connect() 的唯一参数是 selector。此方法可以从 Redux store 接收到全局的 state,然后返回组件中需要的 props。详资料请参考文档:http://cn.redux.js.org/docs/basics/Usage...

展示组件

上面的容器组件中引入了一个展示组件List,我们来看下它的代码:

import React, { Component, PropTypes } from ‘react‘

export default class List extends Component {
    render() {
        return(
            <li className="list-group-item">{this.props.text}</li>
        )
    }
}

List.propTypes = {
    text: PropTypes.string.isRequired
}

从中我们可以发现,展示组件没有connect的方法,数据是通过this.props来获取的,这样的方式能够是数据的变化清晰可查,便于管理和维护。

demo演示

最后我们来看下这个demo:

整个demo的代码我都上传到了我的github,需要的童鞋可以访问:https://github.com/luozhihao/redux-basic...下载

总结

Redux的知识点繁多,这里只做了大概的介绍,剩下的还需要自己不断的摸索和实践。希望本文能够帮助你了解利用redux构建项目的大体流程。

此致敬礼

原创文章,转载请注明来自一个萝卜一个坑 -博客园[http://www.cnblogs.com/luozhihao]

本文地址:http://www.cnblogs.com/luozhihao/p/5660496.html

本文同步发表于:https://segmentfault.com/a/1190000005933397

时间: 2024-10-07 22:03:42

Redux状态管理方法与实例的相关文章

react+redux状态管理实现排序 合并多个reducer文件

这个demo只有一个reducer 所以合并reducer这个demo用不到 ,但是我写出来这样大家以后可以用到,很好用,管理多个reducer,因为只要用到redux就不会只有一个reducer所以这个合并reducer很好用. 需要的技术:react-redux    redux实现状态管理 装饰器:transform-decorators-legacy下载 第一步下载transform-decorators-legacy npm install transform-decorators-l

JSP状态管理 及 Cookie实例

HTTP协议的无状态性 无状态是指,当浏览器发送请求给服务器的时候,服务器响应客户端的请求. 但是当同一个浏览器再次发送请求给了服务器的时候,服务器并不知道它就是刚才那个浏览器. 简单地说,就是服务器不回去记得你,所以就是无状态协议. 保存用户状态的两大机制:Session和Cookie. 什么是Cookie? Cookie:中文名称“小甜饼”,是Web服务器保存在客户端的一系列文本信息. 典型应用一:判定注册用户是否已经登录网站. 典型应用二:“购物车”的处理. Cookie的作用: 对特定对

nextjs的开发使用(二)---引入redux状态管理

在上篇文章中,基于react的nextjs服务端渲染框架学习使用 学习了解了一些关于nextjs的东西,并做了一个小demo,这篇文章将对上篇文章做一个补充,在nextjs中引入redux 安装 // 安装redux相关依赖 yarn add redux redux-saga react-redux // 安装next.js对于redux的封装依赖包 yarn add next-redux-wrapper next-redux-saga yarn add redux react-redux 创建

Mobx | 强大的状态管理工具 | 可以用Mobx来替代掉redux

来源简书 电梯直达 https://www.jianshu.com/p/505d9d9fe36a Mobx是一个功能强大,上手非常容易的状态管理工具.就连redux的作者也曾经向大家推荐过它,在不少情况下你的确可以使用Mobx来替代掉redux. 本教程旨在介绍其用法及概念,并重点介绍其与React的搭配使用. 先来看看最基本的用法. observable和autorun import { observable, autorun } from 'mobx'; const value = obse

状态管理器 redux

简单修改state:let state = { count: 1 } 我们来使用下状态 console.log(state.count); 我们来修改下状态 state.count = 2; console.log(state.count); 实现了状态(计数)的修改和使用了. 上面的有一个很明显的问题:修改 count 之后,使用 count 的地方不能收到通知.我们可以使用发布-订阅模式来解决这个问题. /*------count 的发布订阅者实践------*/第一个版本: let sta

C#实例之简单聊天室(状态管理)

前言        状态管理是在同一页或不同页的多个请求发生时,维护状态和页信息的过程.因为Web应用程序的通信协议使用了无状态的HTTP协议,所以当客户端请求页面时,ASP.NET服务器端都会重新生成一个网页实例.此时,旧网页的任务完成,旧网页的实例也随之消失.这种无状态,意味着客户端用户在浏览器中的一些状态或是对数据的一些修改都将丢失. 为了弥补这种基于web应用程序的固有限制,ASP.NET提供了多种用于管理状态的功能. 简单聊天室 这里运用System.Web命名空间的那些管理状态的类,

(转)Java 的swing.GroupLayout布局管理器的使用方法和实例

摘自http://www.cnblogs.com/lionden/archive/2012/12/11/grouplayout.html (转)Java 的swing.GroupLayout布局管理器的使用方法和实例 GroupLayout 是一个 LayoutManager,它将组件按层次分组,以决定它们在 Container 中的位置.GroupLayout 主要供生成器使用,但也可以手工编码.分组由 Group 类的实例来完成.GroupLayout 支持两种组.串行组 (sequenti

vuex:一篇看懂vuejs的状态管理神器

关于vuex类的新闻最近很多,看到眼热就去查了下资料,然后扯出来一堆flux.redux.state.state之类的概念,以及大型工程必要性之类的.看官方手册也是昏昏然. 然而,我还是弄懂了!我准备从demo出发,以同样的一个最简单的demo,演示两种情况下的代码编写情况: 单纯依赖于vue.js 依赖vue.js,也使用了vuex技术 目的是通过对比引出vuex的概念.优势和劣势.也许这是目前最接地气的vuex的介绍吧:).所以无论如何在了解vuex之前,你必须懂得vue.js(好像废话:)

React构建单页应用方法与实例

React作为目前最流行的前端框架之一,其受欢迎程度不容小觑,从这门框架上我们可以学到许多其他前端框架所缺失的东西,也是其创新性所在的地方,比如虚拟DOM.JSX等.那么接下来我们就来学习一下这门框架是如何构建起一个单页应用的. 前言 首先在学习这门框架前,你需要对以下知识有所了解: 原生JS基础 CSS基础 npm包管理基础 webpack构建项目基础 ES6规范 以上五个知识点也是目前学习其他前端框架所必须了解的前置任务.JS和CSS就不多说了,npm是目前最提倡也是占据主导地位的包管理工具