Redux 和React 进行结合, 就是用React 做UI, 因为Redux中定义了state,并且定义了改变或获取state的方法,完全可以用来进行状态管理,React中就不用保存状态了,它只要渲染UI 界面就可以了,再说,React 本来就是一个view库, 渲染UI也是它的本职工作。那UI中状态发生变化,怎么办? 它可以发送action, 状态改变后,UI也可以通过store.getState 来获取最新的状态。

  这样听起来非常美好,但有个现实的问题需要解决,那就是组件中怎么获取store? 组件中只有获取到store, 它才能dispatch action或get state.  一个很简单的方式,就是层层传递,根组件获取到store, 然后把store 作为props向下传递,一直传递到调用store的子组件。父子之间就是通过props进行通传信的,理论是没有问题,但是如果我们的程序层级嵌套很深的话,怎么办? 所有经过的子组件都要写store ={store} 的字样,非常繁琐,而且如果哪个组件忘记写了,程序还会报错,排查起来也很困难,这显然不是我们要想的。还有一个问题,就是UI 组件中直接包含从store.getState()获得的数据,数据和视图强耦合在一起,不利于视图的重用。这也涉及到React 的一个设计模式, 容器组件和展示组件的分离。

  简单说一下容器组件或展示组件,展示组件很简单,就是展示用的,拿到数据,渲染UI,那容器组件,就是给展示组件拿数据的,它里面包含API的调用, 异步请求等。

  那我们现在需要解决两个问题:

    1, store的传递问题, 子组件中怎么轻松地获得store。

    2, 提供一个容器组件,让它去获取状态,然后把状态注入到展示组件中,而不是展示组件自己获取状态。

  React-Redux库给我们提供了解决以上两个问题的方法:

    对于第一个store传递的问题,它提供了一个Provider组件, 只要把我们应用程序的根组件放到Provider组件中,根组件和它所有的子组件都会自动获取到store,当然,我们要给Provider 组件提供一个属性, 就是store. 假设我们根组件是App,代码如下:

<Provider store ={store}>
    <App />
</Provider>

    对于第二个问题,创建容器组件,它提供一个connect 函数。connect 作为函数,它可以接受展示组件作为参数从而返回一个组件,这里你可能想到了React的高阶组件。高阶组件就可以接受一个展示组件,然后在其内部处理数据,数据处理完成后,再给接受到的展示组件,进行页面渲染。可以看到容器组件是一个高阶组件,我们可以在其内部处理数据,也就是说在这里我们就可以从store中获取数据, 同时在页面中我们还要发送action, 在这里也可以一并获取。

  但是它获取的方式有点不一样,是通过函数进行获取的。 我们的容器组件获取到了store, 也就意味着我们获取到state 和 distpath, 我们要怎么将容器组件获取到的state, 和 dispatch 传递给展示组件,这里用到了两个函数, 一个函数就是把state 映射到 props, 一个是把dispatch 映射到 props, 只要把这两个函数传递给connect 函数就可以了。刚才我们说了,connect 函数可以接受展示组件作为参数,其实它也是一个高阶函数,它先接受这两个映射函数作为参数,返回一个函数,然后再接受一个展示组件,返回一个容器组件。

  把state 映射到props 的函数样式如下, 注意,它接受state作为参数,返回的是一个对象

//  把state映射到props中,组件中的props中就有了users属性
const mapStateToProps = state => (
    {
        users: state.users
    }
) 

  把dispatch映射到props 的函数样式如下, 它接受dispatch作为参数,返回的是一个对象

// 组件的props 就会有fetchUser函数
const mapDispatchToProps = dispatch => {
    return {
        fetchUsers: () => {
            dispatch({type: 'USER_FETCH'});
        }
    }
}

  假设我们的展示组件是Users,那我们就可以conncet 生成容器组件, 如下代码中, UserContainer 就是一个容器组件

const UsersContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Users) 

  当然,mapStateToProps 和 mapDispatchToProps 函数也不是非要写的,如果只向组件中传递state,那只要第一个mapStateToProps 就可以了,mapDispatchToProps 就可以省略。如果只向组件中传递dispath, 那只需要mapDispatchToProps  就可以了,但是第一个参数mapStateToProps 的地方要写为null 如下

const UsersContainer = connect(
    null,
    mapDispatchToProps
)(Users) 

  好了,react 结合redux 内容差不多了,我们就实战一下,把Redux进阶中的随机获取人物信息的实例重构一下, 代码还是从头开始写。 新建一个文件夹ReactAndRedux,然后npm init -y 创建package.json 文件, 再  npm i babel-core babel-loader  babel-preset-env babel-preset-stage-2 babel-preset-react  webpack webpack-dev-server --save-dev  安装webpack, babel 开发依赖, 在项目中要用到React, 所以不要忘记安装babel-preset-react. 创建一个webpack.config.js 用于打包,.babelrc文件使用babel

webpack.config.js 文件如下

const path = require('path');

module.exports = {
    entry: path.join(__dirname, 'src/index.js'),
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
      publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            }
        ]
    }
}

  .babelrc 文件如下:

{"presets" : ["env", "stage-2", "react"]}

  现在再新建一个index.html文件,script标签也应该引入bundle.js打包后的文件,作为骨架

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <body>
        <div id="app"></div>
        <script src='./dist/bundle.js'></script>
      </body>
</body>
</html>

  最后新建 一个src文件夹用到存放我们的源代码,在src文件夹中新建index.js, 作为入口,因为是由Redux来管理我们的状态,所以要建立store.js文件存放我们的store, actions 文件夹来存我们的action, reducers存放我们的reducer. React部分则分成展示组件和容器组件,所以还要新建两个文件夹,containers 和 components 来存放它们。 当然还要安装react ,redux. npm i react redux react-dom react-redux --save. 整个项目文件如下:

  现在测试一下webpack的配置有没有问题 , 在package.json 文件的scripts中,写入"dev": "webpack-dev-server", 现在npm run dev启动服务。

  在index.js文件中写入 document.getElementById('app').innerHTML = 'hello' ,可以看到浏览器中出现了hello, 现在更改hello 为hel , 浏览器hel。 webpack配置没有问题,现在可以安心的写代码了。

  1,定义整个应用状态的state结构

// state 状态
const initalState = {
    user: {
      name: '',
      email: '',
      gender: ''
    },
    status: '',
    statusClass: ''
}

  2 ,确定reducer, 因为我们整个应用就是点击按钮,请求数据,它是一个异步请求, 所以有三个行为,我们要定义对应三个action 的 reducer, 异步用到了 redux-promise-middleware.  这里还要npm install redux-promise-middleware, 同时安装上redux-logger 日志进行记录, 如发送 FETCH_USER action, 那么reducer 对应的三个action 就是FETCH_USER_PENDING,FETCH_USER_FULFILLED,FETCH_USER_REJECTED .  在Reducer文件中新一个userReducer.js文件, 不要忘记export default  语句

// state 状态
const initalState = {
    user: {
      name: '',
      email: '',
      gender: ''
    },
    status: '',
    statusClass: ''
}

// REDCUER
function userReducer(state=initalState, action) {
    const user = {
      name: '',
      email: '',
      gender: ''
    };

    switch (action.type) {
      case 'FETCH_USER_PENDING':
        return {...state, sendingRequest: true, status: 'Pending...', statusClass: 'pending'}
        break;
      case 'FETCH_USER_FULFILLED':
        user.name = `${action.payload.data.results[0].name.first} ${action.payload.data.results[0].name.last}`
        user.email = action.payload.data.results[0].email;
        user.gender = action.payload.data.results[0].gender;
        return {...state, sendingRequest: false, user, status: 'User Recieved', statusClass: 'success'}
      break;
      case 'FETCH_USER_REJECTED':
      return {...state, sendingRequest: false, status: `${action.payload.message}`, statusClass: 'error'}
      default:
        return state
    }
}

export default userReducer;

  3, 现在我们就可以写store了, 直接在store.js中书写代码。

import {createStore, applyMiddleware} from 'redux';
import {createLogger} from 'redux-logger';
import promise from 'redux-promise-middleware';
import userReducer from './reducers/reducer';

const store = createStore(    userReducer,
    applyMiddleware(createLogger(), promise()))

export default store;

  4, action 就简单了,它直接发送一个ajax请求就可以了,在actions文件夹中新建userAction.js文件, 发送请求用到了axios, 这里要安装它, npm install axios --save.  在actions 文件夹下新建userAction.js 文件, 内容如下:

import axios from 'axios';

const fetchUsers = function() {
    return {
        type: 'FETCH_USER',
        payload: axios.get('https://randomuser.me/api/')
    }
}

export default fetchUsers;

  5, redux方面的事情已经完事了,现在再来写react。react 分为了两个部分,展示组件或容器组件。

  容器组件,就是把state 和 dispatch 映射为props, 从而可以在展示组件中使用, 这里要用connect 高阶函数, 同时我们还要命名一个展示组件。 我们在container 文件夹中新建一个userContainer.js 文件,同时在components文件夹新建一个user.js文件。 userContainer.js文件如下

import {connect} from 'react-redux';   // 引入connect函数用来生成容器组件
import User from '../components/user';  // 引入展示组件
import fetchUsers from '../actions/userAction'; // 引入要发送的action

// 把state 映射到props中
const mapStateToProps = state => (
    {
        data: state
    }
)

// 把dispatch 映射到props中
const mapDispatchToProps = dispatch => ({
    fetchUser: () => {
        dispatch(fetchUsers())
    }
})

const UserContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(User)

export default UserContainer;

  现在我们的展示组件的props 有了data属性, 它就是从state中获取的状态, 同时还有了fetchUser 属性,用来发送action, 现在来写user.js 展示组件

import React from 'react';

// es6 的解构赋值来获取属性
const User = ({data, fetchUser}) => (
    <div>
        <button className="btn btn-blue" onClick={fetchUser}>New Random User</button>
        <h2 className ={data.statusClass}>{data.status}</h2>
        <h2>{data.user.name}</h2>
        <h2>{data.user.email}</h2>
        <h2>{data.user.gender}</h2>
    </div>
)

export default User;

  6, ReactDOM 把组件渲染出来,代码写在index.js中。

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';  // 引入Provider 组件,传递store
import UserContainer from './containers/userContainer';  // 引入容器组件
import store from './store';   // 引入store
 // Provider 组件包含容器组件,从而容器组件和其下面的子组件都会获取到
ReactDOM.render(
    <Provider store ={store}>
        <UserContainer />
    </Provider>,
    document.getElementById('app')
)

  启动服务器,页面上只有 一个按钮,当我们点击按钮后,它获取数据

Redux学习(3) ----- 结合React使用的更多相关文章

  1. React+Redux学习笔记:React+Redux简易开发步骤

    前言 React+Redux 分为两部分: UI组件:即React组件,也叫用户自定义UI组件,用于渲染DOM 容器组件:即Redux逻辑,处理数据和业务逻辑,支持所有Redux API,参考之前的文 ...

  2. React Redux学习笔记

    React Router React Router 使用教程 Redux中间件middleware [译]深入浅出Redux中间件 Redux学习之一:何为middleware? ES6 ES6新特性 ...

  3. redux学习

    redux学习: 1.应用只有一个store,用于保存整个应用的所有的状态数据信息,即state,一个state对应一个页面的所需信息 注意:他只负责保存state,接收action, 从store. ...

  4. 【原】redux学习笔记

    上周学习了flux,这周研究了一下redux,其实很早之前都已经在研究他们了,只是之前一直没搞懂,最近这两周可能打通了任督二脉,都算入门了. 写博客的目的主要是做一下笔记,总结一下思路,以及和大家交流 ...

  5. redux学习总结

    redux学习总结 *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !imp ...

  6. (转)2019年 React 新手学习指南 – 从 React 学习线路图说开去

    原文:https://www.html.cn/archives/10111 注:本文根据 React 开发者学习线路图(2018) 结构编写了很多新手如何学习 React 的建议.2019 年有标题党 ...

  7. Redux管理你的React应用

    使用Redux管理你的React应用   因为redux和react的版本更新的比较频繁,博客园这里用的redux版本是1.0.1,如果你关心最新版本的使用技巧,欢迎来我的Github查看(https ...

  8. Redux学习及应用

    Redux学习及应用 一:Redux的来源? Redux 是 JavaScript 状态容器,提供可预测化的状态管理.Redux是由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂 ...

  9. react,react-router,redux+react-redux 构建一个React Demo

    创建初始化应用 加速我们的npm. npm install -g cnpm --registry=https://registry.npm.taobao.org 利用create-react-app ...

随机推荐

  1. [MicroPython]TPYBoardv102自动浇花系统

    1.系统功能 监测土壤湿度.环境温度.光照强度 根据当前环境自动浇水,寒冷天气自动加热土壤 2.所需元器件 TPYBoard板子1块 光敏模块1块 DS18B20模块1块 土壤湿度检测模块1块 杜邦线 ...

  2. SpringBoot分布式 - Dubbo+ZooKeeper

    一:介绍 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务.它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护.域名服务.分布式同步.组服务等. Dubbo是Alib ...

  3. 五子棋(无AI winform gdi+)

    之前无意间在博客园看到一篇用深度学习玩马里奥的文章,于是就想做这个小东西来测试人工智能算法(准备用PYTHON的库,对神经网络的梦已经做了好多年了,但是太难了,一直懒得动它),本来是想用WPF做UI, ...

  4. 小记Java时间工具类

    小记Java时间工具类 废话不多说,这里主要记录以下几个工具 两个时间只差(Data) 获取时间的格式 格式化时间 返回String 两个时间只差(String) 获取两个时间之间的日期.月份.年份 ...

  5. iOS数据存储-钥匙串存储

    2017.11.20 14:41* 字数 227 阅读 678评论 0喜欢 0 钥匙串介绍   1. 表示设备唯一号的标识,在IOS7中要么被禁止使用,要么重新安装程序后两次获取的标识符不一样. 2. ...

  6. 弹性(flex)布局

    五大主流浏览器及其内核:谷歌浏览器:Google Chrome.内核是blink火狐浏览器:Mozilla Firefox.内核是Gecko:欧鹏浏览器:OPera.内核是blink苹果浏览器:Saf ...

  7. 08-webpack的介绍

    在这里我仅仅的是对webpack做个讲解,webpack这个工具非常强大,解决了我们前端很繁琐的一些工具流程繁琐的事情.如果感兴趣的同学,简易还是看官网吧. 中文链接地址:https://www.we ...

  8. 正则校验:微信号,qq号,邮箱

    java判断微信号.手机.名字的正则表达 - willgos - 博客园https://www.cnblogs.com/solossl/p/5813106.html 微信号正则校验,qq正则,邮箱正则 ...

  9. MySQL Limit优化(转)

    原文:http://bbs.landingbj.com/t-0-240894-1.html 首先,我们看一个分页SQL: SELECT time,pageFROM `l_not_200_page`WH ...

  10. 手机移动端input date placehoder不显示

    要解决这个问题,我们可以伪造一个placehoder,通过css跟js来解决这个问题. 为什么要用js的原因是因为当你选择了时间之后,placehoder的文字没有清除掉,所以我们就需要把这个伪造的p ...