React-redux: React.js 和 Redux 架构的结合
通过Redux 架构理解我们了解到 Redux 架构的 store、action、reducers 这些基本概念和工作流程。我们也知道了 Redux 这种架构模式可以和其他的前端库组合使用,而 React-redux 正是把 Redux 这种架构模式和 React.js 结合起来的一个库。
Context
在 React 应用中,数据是通过 props 属性自上而下进行传递的。如果我们应用中的有很多组件需要共用同一个数据状态,可以通过状态提升的思路,将共同状态提升到它们的公共父组件上面。但是我们知道这样做是非常繁琐的,而且代码也是难以维护的。这时会考虑使用 Context,Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。也就是说在一个组件如果设置了 context,那么它的子组件都可以直接访问到里面的内容,而不用通过中间组件逐级传递,就像一个全局变量一样。
在 App -> Toolbar -> ThemedButton 使用 props 属性传递 theme,Toolbar 作为中间组件将 theme 从 App 组件 传递给 ThemedButton 组件。
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
// 因为必须将这个值层层传递所有组件。
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
使用 context,就可以避免通过中间元素传递 props 了
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light'); class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
} // 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
} class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
虽然解决了状态传递的问题却引入了 2 个新的问题。
1. 我们引入的 context 就像全局变量一样,里面的数据可以被子组件随意更改,可能会导致程序不可预测的运行。
2. context 极大地增强了组件之间的耦合性,使得组件的复用性变差,比如 ThemedButton 组件因为依赖了 context 的数据导致复用性变差。
我们知道,redux 不正是提供了管理共享状态的能力嘛,我们只要通过 redux 来管理 context 就可以啦,第一个问题就可以解决了。
Provider 组件
React-Redux 提供 Provider 组件,利用了 react 的 context 特性,将 store 放在了 context 里面,使得该组件下面的所有组件都能直接访问到 store。大致实现如下:
class Provider extends Component {
// getChildContext 这个方法就是设置 context 的过程,它返回的对象就是 context,所有的子组件都可以访问到这个对象
getChildContext() {
return {
store: this.props.store
};
}
render() {
return this.props.children;
}
}
Provider.childContextTypes = {
store: React.PropTypes.object
}
那么我们可以这么使用,将 Provider 组件作为根组件将我们的应用包裹起来,那么整个应用的组件都可以访问到里面的数据了
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import todoApp from './reducers';
import App from './components/App';
const store = createStore(todoApp);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
展示(Dumb Components)组件和容器(Smart Components)组件
还记得我们的第二个问题吗?组件因为 context 的侵入而变得不可复用。React-Redux 为了解决这个问题,将所有组件分成两大类:展示组件和容器组件。
展示组件
展示组件有几个特征
1. 组件只负责 UI 的展示,没有任何业务逻辑
2. 组件没有状态,即不使用 this.state
3. 组件的数据只由 props 决定
4. 组件不使用任何 Redux 的 API
展示组件就和纯函数一样,返回结果只依赖于它的参数,并且在执行过程里面没有副作用,让人觉得非常的靠谱,可以放心的使用。
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Title extends Component {
static propTypes = {
title: PropTypes.string
}
render () {
return (
<h1>{ this.props.title }</h1>
)
}
}
像这个 Title 组件就是一个展示组件,组件的结果完全由外部传入的 title 属性决定。
容器组件
容器组件的特征则相反
1. 组件负责管理数据和业务逻辑,不负责 UI 展示
2. 组件带有内部状态
3. 组件的数据从 Redux state 获取
4. 使用 Redux 的 API
你可以直接使用 store.subscribe() 来手写容器组件,但是不建议这么做,因为这样无法使用 React-redux 带来的性能优化。
React-redux 规定,所有的展示组件都由用户提供,容器组件则是由 React-Redux 的 connect() 自动生成。
高阶组件 Connect
React-redux 提供 connect 方法,可以将我们定义的展示组件生成容器组件。connect 函数接受一个展示组件参数,最后会返回另一个容器组件回来。所以 connect 其实是一个高阶组件(高阶组件就是一个函数,传给它一个组件,它返回一个新的组件)。
import { connect } from 'react-redux';
import Header from '../components/Header';
export default connect()(Header);
上面代码中,Header 就是一个展示组件,经过 connect 处理后变成了容器组件,最后把它导出成模块。这个容器组件没有定义任何的业务逻辑,所有不能做任何事情。我们可以通过 mapStateToProps 和 mapDispatchToProps 来定义我们的业务逻辑。
import { connect } from 'react-redux';
import Title from '../components/Title';
const mapStateToProps = (state) => {
return {
title: state.title
}
}
const mapDispatchToProps = (dispatch) => {
return {
onChangeColor: (color) => {
dispatch({ type: 'CHANGE_COLOR', color });
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Title);
mapStateToProps 告诉 connect 我们要取 state 里的 title 数据,最终 title 数据会以 props 的方式传入 Title 这个展示组件。
mapStateToProps 还会订阅 Store,每当 state 更新的时候,就会自动执行,重新计算展示组件的参数,从而触发展示组件的重新渲染。
mapDispatchToProps 告诉 connect 我们需要 dispatch action,最终 onChangeColor 会以 props 回调函数的方式传入 Title 这个展示组件。
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) // 将 Store 的 state 和容器组件的 state 传入 mapStateToProps
: {} // 判断 mapStateToProps 是否传入
let dispatchProps = mapDispatchToProps
? mapDispatchToProps(store.dispatch, this.props) // 将 dispatch 方法和容器组件的 state 传入 mapDispatchToProps
: {} // 判断 mapDispatchToProps 是否传入
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
}
render () {
// 将 state.allProps 展开以容器组件的 props 传入
return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}
小结
至此,我们就很清楚了,原来 React-redux 就是通过 Context 结合 Redux 来实现 React 应用的状态管理,通过 Connect 这个高阶组件来实现展示组件和容器组件的连接的。

更多精彩内容,欢迎关注微信公众号~
React-redux: React.js 和 Redux 架构的结合的更多相关文章
- 配置react, redux, next.js环境
1. react https://reactjs.org/docs/add-react-to-a-new-app.html npm install -g create-react-app create ...
- react项目中引入了redux后js控制路由跳转方案
如果你的项目中并没有用到redux,那本文你可以忽略 问题引入 纯粹的单页面react应用中,通过this.props.history.push('/list')就可以进行路由跳转,但是加上了redu ...
- Flux --> Redux --> Redux React 入门
本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...
- Flux --> Redux --> Redux React 基础实例教程
本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...
- react系列(四)Redux基本概念和使用
Redux基本概念和使用 先从Flux开始 先放一个Flux官网的链接.需要fq. Flux是Facebook提出的一种构建客户端网页应用的应用架构,它是一种抽象程度很高的设计模式,鼓励单向数据流. ...
- Flux --> Redux --> Redux React 入门 基础实例使用
本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...
- [Redux] React Todo List Example (Adding a Todo)
Learn how to create a React todo list application using the reducers we wrote before. /** * A reduce ...
- Redux & React & react-redux
Redux Redux & React & react-redux https://redux.js.org/ https://redux.js.org/api https://red ...
- react 脚手架 及路由和 redux
前提是我们需要下载 nodejs 使用 npm 下载 react 的脚手架,react-router-dom,redux 全局下载 react 的脚手架:npm i create-react-app ...
- 4 react 简书 引入 redux 的 combineReducers 对 redux 数据进行管理
1. src 下的 common 下的 header 创建 store 文件夹 下创建 reducer.js # src/common/header/store/reducer.js const st ...
随机推荐
- Windows下的GUI 库
Windows 下的 GUI 解决方案比较多: 基于 C++ 的有 Qt.MFC.WTL.wxWidgets.DirectUI.Htmlayout: 基于 C# 的有 WinForm.WPF: 基于 ...
- 安装oracle 11g 客户端,检查过程中报物理内存不足的解决
今早接到同事电话,说安装oracle 11g客户端的时候,在检查先决条件的时候,报错,说内存不足,但是本机的内存是2G,肯定够用:如图: 找了一圈,原来Oracle执行先决条件检查是依赖c$共享,很多 ...
- Linux安装完后的调优(linux 6)
1:关闭 SELinux 方法一: #sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config #替换文本参数 ...
- git本地仓库目录问题
git安装后修改默认的路径:每次打开git bash后都会进入这个目录 https://blog.csdn.net/weixin_39634961/article/details/79881140 在 ...
- 响应式WEB页面开发实践
转自:https://github.com/markyun/My-blog/issues/27 最近得到一个新任务单,让我用一套页面适应所有主流终端(Android.iPhone.iPad.PC),而 ...
- 关于js中的比较时遇到的坑
关于JavaScript中比较遇到的坑 当你的要比较数字的大小但是你的数字确是字符串时,就会出错比如说: console.log('5' > '6') // fasle consloe.log( ...
- STM32 CAN 发送和接收 寄存器变化过程
发送:
- 最长递增子序列-dp问题
Longest Increasing Subsequence The longest increasing subsequence problem is to find a subsequence o ...
- DOS命令编译JAVA程序
上篇文章给大家写了怎么安装JDK配置,现在这篇文章我们就来学习下怎么在DOS命令下编译JAVA程序,以后没编译器都可以直接编译啦(嘻嘻!) 我这里就用永远的 “Hello wrold!”来演示下吧. ...
- xpath_note - Ethan Lee
https://ethan2lee.github.io/ XPath概览 XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言.它最初是用来搜寻X ...