首先,一张 Redux 解释图镇楼:

【回顾】Redux 的核心: store 是什么?(createStore 函数的实现)
const store = createStore(reducer);

store 是一个对象,包含3个方法:getStatedispatchsubscribe

// createStore 函数实现
const createStore = (reducer) => {
let state;
let listeners = []; const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
}
const subscribe = (listener) => { // listener 就是一个要执行的函数
listeners.push(listener);
return () => { // 采用柯里化方式注销监听器,用法:store.subscribe(listener)();
listeners = listeners.filter(l => l != listener);
}
} dispatch({}); // 初始化state return { getState, dispatch, subscribe }
}

由函数可知,当用户 dispatch 一个 action 时,会自动调用 reducer 从而得到最新的 state,该 state 可通过 getState 函数获取,并且会执行所有已注册的函数。

所以,redux 的套路就是(参考 React小书 ):

// 定一个 reducer
function reducer (state, action) {
/* 初始化 state 和 switch case */
} // 生成 store
const store = createStore(reducer) // 监听数据变化重新渲染页面,即更新状态的过程
store.subscribe(() => renderApp(store.getState())) // 首次渲染页面
renderApp(store.getState()) // 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)
【问题】:React 和 Redux 之间是如何连接?

从图中可以看到,Store 通过 Provider 传递给了我们的 React 组件,因此,使得组件能够获取到 store。那么它是如何将做到的呢?

为了弄明白 React 和 Redux 之间是如何连接的,我们需要了解以下一些内容(参考 React小书 ):

一、背景:React 中父组件 context 的作用,用以摆脱状态提升

在 React 中,父组件使用 getChildContext(),可以将 store 放到它的 context 里面,相当于给子组件设置了一个全局变量,这样每个子组件就都可以获取到 store。

// 父组件
class Index extends Component {
// 提供 context 的组件必须提供 childContextTypes 作为 context 的声明和验证
static childContextTypes = {
store: PropTypes.object
} // 一个组件可以通过 getChildContext 方法返回一个对象,这个对象就是子树的 context
getChildContext () {
return { store }
} render () {
return (
<div>
<Header />
<Content />
</div>
)
}
} // 子组件
class Header extends Component {
// 声明想要的 context 里面的哪些状态,以便通过 this.context 进行访问
// 子组件要获取 context 里面的内容的话,就必须写 contextTypes 来声明和验证你需要获取的状态的类型
static contextTypes = {
store: PropTypes.object
} constructor () {
super()
this.state = { themeColor: '' }
} componentWillMount () {
this._updateThemeColor()
} _updateThemeColor () {
// 子组件可以访问到父组件 context 里面的内容
const { store } = this.context
const state = store.getState()
this.setState({ themeColor: state.themeColor })
} render () {
return (
<h1 style={{ color: this.state.themeColor }}>React.js 小书</h1>
)
}
}

如果一个组件设置了 context,那么它的子组件都可以直接访问到里面的内容,它就像这个组件为根的子树的全局变量。任意深度的子组件都可以通过 contextTypes 来声明你想要的 context 里面的哪些状态,然后可以通过 this.context 访问到那些状态。

context 存在的问题:首先,它是一个试验性的API,不稳定,可能会改变,虽然好多库都用到了这个特性;其次它是脆弱的,如果在层级中的任何一个组件执行了 shouldComponentUpdate 返回 false,context 则不会传递给其之后所有的子组件。

二、react-redux 的诞生

因为 context 是一个比较危险的特性,我们不想在自己写组件的时候被其污染,我们需要将其剥离出来,因此,react-redux 诞生了,其中的 Provider 以及 connect 就帮助我们将 React 的组件和 Redux 的 store 进行了连接。

1. Provider 的实现

作用:充当父组件的作用,把 store 放到自己的 context 里面,让子组件 connect 的时候获取。

export class Provider extends Component {
static propTypes = {
store: PropTypes.object,
children: PropTypes.any
} static childContextTypes = {
store: PropTypes.object
} getChildContext () {
return {
store: this.props.store
}
} render () {
return (
<div>{this.props.children}</div>
)
}
}
2. 高阶组件 connect(connect 实现)

高阶组件:高阶组件是一个接受一个组件为参数,并返回一个被包装过的组件的函数,即返回传入props的原组件。

connect 的作用:和 React 的 context 打交道,将 context 中的数据取出来,并以 prop 的形式传递给 Dumb 组件。

const mapStateToProps = (state) => { themeColor: state.themeColor }
const mapDispatchToProps = (dispatch) => ({
onSwitchColor(color) => {
dispatch({ type: 'CHANGE_COLOR', themeColor: color })
}
}); // connect 实现
// connect 接受 mapStateToProps 和 mapDispatchProps 参数后,返回的函数是高阶组件,该高阶组件接受一个组件作为参数,然后用 Connect 包装之后返回
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
class Connect extends Component {
static contextTypes = {
store: PropTypes.object
} constructor () {
super()
this.state = {
allProps: {}
}
} componentWillMount () {
const { store } = this.context
this._updateProps()
store.subscribe(() => this._updateProps())
} _updateProps () {
const { store } = this.context
let stateProps = mapStateToProps
? mapStateToProps(store.getState(), this.props)
: {} // 防止 mapStateToProps 没有传入
let dispatchProps = mapDispatchToProps
? mapDispatchToProps(store.dispatch, this.props)
: {} // 防止 mapDispatchToProps 没有传入
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
} render () {
return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}

connect 接口:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(Component)

三、react-redux 的性能优化

react-redux性能优化之reselect

三、文章参考

React小书

Redux使用小结

React-redux深入理解的更多相关文章

  1. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  2. react+redux官方实例TODO从最简单的入门(6)-- 完结

    通过实现了增-->删-->改-->查,对react结合redux的机制差不多已经了解,那么把剩下的功能一起完成吧 全选 1.声明状态,这个是全选状态 2.action约定 3.red ...

  3. react+redux官方实例TODO从最简单的入门(1)-- 前言

    刚进公司的时候,一点react不会,有一个需求要改,重构页面!!!完全懵逼,一点不知道怎么办!然后就去官方文档,花了一周时间,就纯react实现了页面重构,总体来说,react还是比较简单的,由于当初 ...

  4. react+redux教程(四)undo、devtools、router

    上节课,我们介绍了一些es6的新语法:react+redux教程(三)reduce().filter().map().some().every()....展开属性 今天我们通过解读redux-undo ...

  5. React + Redux 入坑指南

    Redux 原理 1. 单一数据源 all states ==>Store 随着组件的复杂度上升(包括交互逻辑和业务逻辑),数据来源逐渐混乱,导致组件内部数据调用十分复杂,会产生数据冗余或者混用 ...

  6. 【原】react+redux实战

    摘要:因为最近搞懂了redux的异步操作,所以觉得可以用react+redux来做一个小小的项目了,以此来加深一下印象.切记,是小小的项目,所以项目肯定是比较简单的啦,哈哈. 项目效果图如图所示:(因 ...

  7. webpack+react+redux+es6

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  8. react+redux构建淘票票首页

    react+redux构建淘票票首页 描述 在之前的项目中都是单纯的用react,并没有结合redux.对于中小项目仅仅使用react是可以的:但当项目变得更加复杂,仅仅使用react是远远不够的,我 ...

  9. React+Redux开发实战项目【美团App】,没你想的那么难

    README.md 前言 开始学习React的时候,在网上找了一些文章,读了官网的一些文档,后来觉得React上手还是蛮简单的, 然后就在网上找了一个React实战的练手项目,个人学完之后觉得这个项目 ...

  10. 详解react/redux的服务端渲染:页面性能与SEO

        亟待解决的疑问 为什么服务端渲染首屏渲染快?(对比客户端首屏渲染)   react客户端渲染的一大痛点就是首屏渲染速度慢问题,因为react是一个单页面应用,大多数的资源需要在首次渲染前就加载 ...

随机推荐

  1. MyBatis动态创建表

    转载请注明出处:https://www.cnblogs.com/Joanna-Yan/p/9187538.html 项目中业务需求的不同,有时候我们需要动态操作数据表(如:动态建表.操作表字段等).常 ...

  2. Linux系统下apt-get命令无法安装常见软件包?

  3. Spring实现WebSocket通信

    WebSocket是两个应用之间全双工的通信通道.WebSocket最常见的应用场景是实现服务器和基于浏览器的应用之间的通信. 浏览器中的javascript客户端开启一个到服务器的连接,服务器通过这 ...

  4. web程序顺序

    1.org.apache.catalina.core.AprLifecycleListener init 2.Apache Tomcat Native library which allows opt ...

  5. JAVA----线程初级

    一.认识多任务.多进程.单线程.多线程 要认识多线程就要从操作系统的原理说起.   以前古老的DOS操作系统(V 6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情.比如你在copy东西 ...

  6. 使用U盘为龙芯笔记本安装操作系统

    摘要:在没有光驱的情况下,可以使用dd命令或者ultraISO软件制作Linux安装U盘,方法适合龙芯和X86.AMD64的设备. 前段时间,由于开发需要,拿到了一部龙芯3A3000的笔记本.出厂的安 ...

  7. boostrap 日期插件(带中文显示)

    不知道大家用什么样的日期插件,我最近用了bootstrap插件,整理了下,分享给大家,第四部分是我找的插件源码,可以省去大家的找的时间,按1-3的步骤用就好了,有些样式上的小问题就自己调一调: (1) ...

  8. MySQL 性能优化的最佳20多条经验分享(收藏)

    1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的.当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一 ...

  9. RedLock.Net - 基于Redis分布式锁的开源实现

    工作中,经常会遇到分布式环境中资源访问冲突问题,比如商城的库存数量处理,或者某个事件的原子性操作,都需要确保某个时间段内只有一个线程在访问或处理资源. 因此现在网上也有很多的分布式锁的解决方案,有数据 ...

  10. 解决getElementsByClassName()在IE8下的兼容问题

    getElementsByClassName,这个方法让我们可以通过 class 属性中的类名来访问元素,但是IE9 以下的浏览器不支持 .为解决这个问题,我们写一个兼容函数 getByClass() ...