一、context实现数据传递

  在react中,props和state都可以设置数据。不同的是,props借助组件属性传递数据但不可以渲染组件,它相对来说是“静态的”;state可以监听事件来修改数据并重新渲染组件,它相对来说是“动态的”。要想实现组件间传递数据并且可以根据state重新渲染组件,就必须一层层地传递props和this.state。

  redux借助context这个全局API,以及内置的getChildContext方法简化了数据传递。context是一个全局的变量,getChildContext让react有能力向context中写入数据,并让当前组件的所有子组件能够从中获取到数据。

  如果在context中传递state状态,那么context的数据就会根据state状态的变化而变化,子组件也会跟着被重新渲染。

import React from 'react'
import PropTypes from 'prop-types' class SubOneChild extends React.Component{
static contextTypes = {
number: PropTypes.number
};
render(){
return <h2> { this.context.number } </h2>
}
} class SubOne extends React.Component{
static contextTypes = {
number: PropTypes.number
};
render(){
console.log(this.context);
return (
<div>
<h4> SubOne: { this.context.number }</h4>
<SubOneChild />
</div>
)
}
} class App extends React.Component{
// 使用context必须要对数据类型进行校验
static childContextTypes = {
number: PropTypes.number
};
constructor(props){
super(props);
this.state = {
number: 10
}
}
// 使用getChildContext向context传递数据
getChildContext(){
return this.state
}
handleChange(){
this.setState({
number: this.state.number + 1
})
}
render(){
return (
<div>
<h2>App: { this.state.number }</h2>
<button onClick={()=>this.handleChange()}>点击增加</button>
<SubOne />
</div>
)
}
}
export default App

  上例并没有使用props传递this.state.number,而是通过context实现了数据的传递。它将数据传递简化为两步:第一步,父组件向context注入state数据并规定context数据协议,第二部,任意层级的子组件根据同样的context数据协议获取数据。

二、实现同步react-redux

  react-redux做了两步:将整个ReactDOM要渲染的组件用Provider包裹起来,只用Provider向context注入state数据;任意组件通过connect从props中获取state数据和sction事件。

// src/my-react-redux.js
import React from 'react'
import PropTypes from 'prop-types' // 1.仿写redux
export function createStore(reducer) {
let currentState = {};
let currentListeners = [];
function getState() {
return currentState;
}
function subscribe(listener) {
currentListeners.push(listener);
}
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(v=>v())
}
dispatch({type: '@#$%^&*('}); // 执行一遍获取默认state
return { getState, subscribe, dispatch }
} // 2.仿写react-redux // Provider: 把store写到context里,全局只包裹一次,这样所有的子元素都可以取到store export class Provider extends React.Component{
  // 规定数据协议
static childContextTypes = {
store: PropTypes.object
};
  // 从Provider属性中获取过来store
constructor(props, context){
super(props, context);
this.store = props.store;
}
  // 向context中传递当前组件的store,以允许任意子组件都可以获取这个store
getChildContext(){
return { store: this.store }
}
  // 返回被包裹的组件
render(){
return this.props.children
}
} // connect接收组件,更新redux数据放到组件的属性里 // export function connect(mapStateToProps, mapDispatchToProps) {
// return function (WrapComponent) {
// return class ConnectComponent extends React.Component{}
// }
// } // 上述带参数的装饰器的简便写法
export const connect = (mapStateToProps=state=>state, mapDispatchToProps={})=>(WrapComponent)=>{
return class ConnectComponent extends React.Component{
    // 规定context数据协议
static contextTypes = {
store: PropTypes.object
};
    // 注意要继承context,在state中设置props参数来接收最外层父组件向context中传递的数据
constructor(props, context){
super(props, context);
this.state = {
props: {}
}
}
    // 获取store,并调用store的监听函数,来监听一个事件;监听后要执行一次更新来渲染一次组件
componentDidMount(){
const { store } = this.context;
store.subscribe(()=>this.update()); // 监听数据变化
this.update();
}
    // 主函数,主要调用store.dispatch(action)方法来更新store以及中间组件ConnectComponent的状态ConnectComponentConCthis.state.props
update(){
const { store } = this.context;
const stateProps = mapStateToProps(store.getState());
const dispatchProps = ConnectComponent.bindActionCreator(mapDispatchToProps, store.dispatch);
this.setState({
          // 因为props会被重写,一定要注意这里的顺序
props: {
...this.state.props,
...stateProps,
...dispatchProps
}
})
}
static bindActionCreator(mapDispatchToProps, dispatch){
let event = {};
Object.keys(mapDispatchToProps).forEach(v=>{
let func = mapDispatchToProps[v];
event[v] = (...args)=>dispatch(func(...args)); // 注意把func的参数传进来,用dispatch调用func
});
return event
}
render(){// 将中间组件ConnectComponent的state透传给被包裹的组件
return <WrapComponent { ...this.state.props } />
}
}
};

  把reducer重写一下:

// src/my-reducer.js

const ADD = 'ADD';
const REDUCE = 'REDUCE';
export function reducer(state=0, action) {
// action都是事件类型
switch(action.type){
case ADD:
return state + 1;
case REDUCE:
return state - 1;
default:
return 10
}
}
export function add() {
return { type: ADD }
}
export function reduce() {
return { type: REDUCE }
}

  app.js中内容改写为:

// src/my-app.js
import React from 'react'
import { connect } from "./my-react-redux";
import { add, reduce } from "./my-reducer"; @connect(state=>({number:state}))
class SubOneChild extends React.Component{
render(){
return <p> { this.props.number } </p>
}
}
@connect(state=>({number:state}))
class SubOne extends React.Component{
render(){
return (
<div>
<h3> SubOne: { this.props.number }</h3>
<SubOneChild />
</div>
)
}
}
@connect(state=>({number:state}), { add, reduce}) // 以对象的方式获取state
class App extends React.Component{
render(){
// console.log(this.props);
return (
<div>
<h1>App: { this.props.number }</h1>
<button onClick={this.props.add}>点击增加</button>
<SubOne />
</div>
)
}
}
export default App

  在index.js中引入App。

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { reducer } from './my-reducer'
import {createStore, Provider} from './my-react-redux'
import App from './my-app' const store = createStore(reducer);
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById('root')
);

  其大致流程图如下:

  

前端(十二):react-redux实现逻辑的更多相关文章

  1. 前端(十):使用redux管理数据

    react本身能够完成动态数据的监听和更新,如果不是必要可以不适用redux. 安装redux: cnpm install redux --save,或者yarn add redux. 一.react ...

  2. 实例讲解基于 React+Redux 的前端开发流程

    原文地址:https://segmentfault.com/a/1190000005356568 前言:在当下的前端界,react 和 redux 发展得如火如荼,react 在 github 的 s ...

  3. Web 前端开发精华文章推荐(jQuery、HTML5、CSS3)【系列十二】

    2012年12月12日,[<Web 前端开发人员和设计师必读文章>系列十二]和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HT ...

  4. Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十二】

    <Web 前端开发精华文章推荐>2014年第一期(总第二十二期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML ...

  5. CTF---Web入门第十二题 程序逻辑问题

    程序逻辑问题分值:20 来源: 实验吧 难度:中 参与人数:6909人 Get Flag:1993人 答题人数:2070人 解题通过率:96% 绕过 解题链接: http://ctf5.shiyanb ...

  6. 前端笔记之React(五)Redux深入浅出

    一.Redux整体感知 Redux是JavaScript状态管理容器,提供了可被预测状态的状态管理容器.来自于Flux思想,Facebook基于Flux思想,在2015年推出Redux库. 中文网站: ...

  7. react第十二单元(react路由-使用react-router-dom-认识相关的组件以及组件属性)

    第十二单元(react路由-使用react-router-dom-认识相关的组件以及组件属性) #课程目标 理解路由的原理及应运 理解react-router-dom以及内置的一些组件 合理应用内置组 ...

  8. react第二十单元(react+react-router-dom+redux综合案例2)

    第二十单元(react+react-router-dom+redux综合案例2) #课程目标 #知识点 #授课思路 #案例和作业

  9. react第十八单元(redux中间件redux-thunk,redux工程目录的样板代码,规范目录结构)

    第十八单元(redux中间件redux-thunk,redux工程目录的样板代码,规范目录结构) #课程目标 中间件:中间件增强redux的可扩展性,实现功能复用的目的. redux-thunk异步逻 ...

随机推荐

  1. BZOJ 1150--数据备份(链表&堆&贪心)

    1150: [CTSC2007]数据备份Backup Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2572  Solved: 1038[Submit ...

  2. 算法图解学习笔记01:二分查找&大O表示法

    二分查找 二分查找又称折半查找,其输入的必须是有序的元素列表.二分查找的基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止:如果x<a[ ...

  3. centos7嵌入式环境搭建

    1. 在网上搜索下载交叉编译器arm-linux-gcc文件,我下载的是:arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 ...

  4. java翻译到mono C#实现系列(3) 获取手机设备信息(残缺,)

    using System; using Android.App; using Android.Content; using Android.Runtime; using Android.Views; ...

  5. 数据库主键ID生成策略

    前言: 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,下面介绍一些常见的ID生成策略. Sequence ID UUID GUID COMB Snowflake 最开始的自增ID为了实现分库 ...

  6. Linux系统编程:socket网络编程(操作篇)

    一.问题思考 问1.网络通信应用在什么场合?通信的前提是什么? 答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信.通信的前提是如何标识通信进程的唯一,由于不同主机的进程极 ...

  7. redis 数据库迁移

    老大让把 一台机器上 redis 中所有的数据,迁移到另一台机器上 查了一下可以拷贝 rdb 文件, 此方法只适用于迁移到一个新的库, 迁移到正在使用的库就不行了, 而且 rdb 里面是所有的 db, ...

  8. python-锁机制

    锁 Lock() Lock(指令锁)是可用的最低级的同步指令.Lock处于锁定状态时,不被特定的线程拥有.Lock包含两种状态——锁定和非锁定,以及两个基本的方法. 可以认为Lock有一个锁定池,当线 ...

  9. PHP之string之ord()函数使用

    ord (PHP 4, PHP 5, PHP 7) ord - Return ASCII value of character ord - 返回字符的 ASCII 码值 Description int ...

  10. [作业] Python入门基础---购物车小程序

    1.购物车小程序: 1.1用户输入工资取60% 1.2打印输出商品菜单 1.3由用户输入数字选择 #__author:Mifen #date: 2018/11/27 # 购物车程序 #把工资作为账户的 ...