参考视频:黑马程序员 | redux全局状态管理
本篇文章总结了React 中数据传值的几种常用的方式,刚学React 不久,总结记录一下以便自己复习回顾,同时也希望能帮到有需要的人。有关Vue传值可以查看我的另一篇博文Vue中的数据传值总结
# 一、传统传值方式
# 1. 1 正向传值:父传子
父组件直接在引用的时候传递值
...
constructor(){
super()
this.state = {
msg: '苹果'
}
}
const list = {
id: 1,
name: 'zs'
}
render(){
<Son aa='123' msg={ this.state.msg } {...list}></Son>
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
子组件直接使用props
...
render(){
return <div>
{this.props.aa}
{this.props.msg}
{this.props.id}---{this.props.name}
</div>
}
2
3
4
5
6
7
8
# 1.2 逆向传值:子传父
这个原理跟父传子的时候极为相似,只不过正向传值传的是值,而逆向传值传的是回调函数
父组件先定义好赋值函数,并在调用子组件时将函数作为参数传递过去
...
export default class Parents extends Component {
constructor() {
super()
this.state = {
text: ''
}
}
fun = (text) => {
this.setState({
text: text
})
}
render() {
return (
<div>
子组件传递给我的数据是:{this.state.text}
<hr />
<Son fuFun={this.fun}></Son>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
子组件使用props
调用回调函数,并通过bind 传递this 和参数
...
export default class Son extends Component {
constructor() {
super()
this.state = {
text: '我是子组件的数据'
}
}
render() {
return (
<div>
<button onClick={this.props.fuFun.bind(this, this.state.text)}>点我传值 </button>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
注意:
- 这里的难点就是this 指向问题,把this 指向搞懂了就好理解
- 父组件定义回调函数时,需要用箭头函数方式定义,也是为了上面的this 指向问题
- 子组件调用回调函数的时候需要注意,不要加
()
执行,还要传递this
# 1.3 同级传值:兄弟间
最传统的兄弟间传值是,先将值传递给共同的父组件,然后再由父组件将值传递给兄弟组件
# 二、传统Redux
# 2.1 Redux 概述
1. 什么是Redux
Redux 是一个用于JavaScript 状态容器,提供可预测的状态管理
Redux 可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试
Redux 除了和React 一起使用外,还支持其他界面库,而且他体小精悍(只有2KB)
2. React 的设计初衷
随着JavaScript 单页面开发日趋复杂,JavaScript 需要管理更多的state(状态),这些state 可能包括服务器响应、缓存数据、本地生成未持久化到服务器的数据,也包括UI 状态等
管理不断变化的state 非常麻烦,如果一个model 的变化会引起另一个model 变化,那么当view 变化时,就可能引起对应model 以及另一个model 的变化,一次可能会引起另一个view 的变化(经典套娃现象),所以机会产生混乱,而Redux 就是为了去解决这个问题的
# 2.2 Redux 三大核心
1. 单一数据源
整个应用的state 被存储在一个Object tree
中,并且这个Object tree
只存在于唯一一个store 中
2. State 是只读的
唯一改变state 的方法就是触发action,action 是一个用于描述已发生事件的普通对象
这样确保了视图和网络请求都不能直接去修改state,相反,他们只能表达想要修改的意图,因为所有的修改都被集中化处理,并且严格按照一个接一个的顺序执行
想要执行只能通过调用以下方法
store.dispatch({type: 'COMPLETE_TODO', index: 1})
3. 使用纯函数来执行修改
为了描述action 如何改变State tree
,你要去编写一个reducer
Reducers 只是一些纯函数,他接收先前的state 和action,并且返回新的state,它可以复用,控制顺序,传入附加操作等
# 2.3 Redux 的组成
1. State -状态
就是我们传递的数据,那么我们在用Redux 开发项目的时候,大致可以吧state 分为三类
- DomainDate:可以理解为服务端的数据,比如:获取用户的信息,商品的列表等等
- UI State:决定当前的UI 决定展示的状态,比如:弹框的显示隐藏,受控组件等等
- App State: App 级别的状态比如:当前是否请求loading,当前路由信息等可能被多个组件去使用到的状态
2. Action -事件
Action 是把数据从应用传到store 的载体,他是store 数据的唯一来源,我们可以通过store.dispatch()
将action 传递给store
action有以下特点:
- action 的本质就是一个JavaScript 的普通对象
- action 对象内部必须要有一个type 属性来表示要执行的动作
- 多数情况下,这个type 会被定义成字符串常量
- 除了type 字段之外,action 的结构随意进行定义
- 我们在项目中,更喜欢用action 创建函数(就是常见action 的地方)
- 只是描述了有事情要发生,并没有描述如何去更新state
3. Reducer
Reducer 本质就是一个函数,他用来相应发送过来的actions,然后经过处理,把state 发送给store 的
注意:在Reducer 函数中,需要return 返回值,这样store 才能接收到数据,函数会接收到两个参数,第一个参数是初始化state,第二个参数是action
4. Store
Store就是action 与reducer 联系到一起的对象
主要职责:
- 维持应用的state
- 提供
getState()
方法获取state - 通过
dispatch()
方法发送action - 通过
subscribe()
方法注册监听 - 通过
subscribe()
返回值来注销监听
# 2.4 Redux 入门案例
- 构建action,通过创建一个函数,然后返回一个对象,注意一定需要携带type 属性
- 构建reducer ,用来相应action,然后通过return 把数据传回给store
- 利用
createStore
方法来构建store,构建的时候传递我们写好的reducer - 利用
store.subscript()
注册监听 - 当我们利用
store.dispatch()
发送一个action 的时候就能触发我们的监听了,在里面利用store.getState()
就能拿到值
# 三、React-redux(推荐)
# 3.1 React-redux 概述
- react-redux 是redux 官方推出用于配合React 的绑定组件库
- react-redux 有两个重要的组成部分
- provider:这个组件能使你整个App 都能获取到store 中的数据
- connect:这个方法能够使组件跟store 来进行关联
- 实现原理类似HOC 高阶组件
# 3.2 React-redux 主要组成
1. Provider
- Provider 是包裹根组件最外层,使素有的子组件都能拿到state
- Provider 接收store 作为props, 然后通过context 往下传递,这样react 中任何组件都能通过context 获取到store
2. connect
- Provider 内部组件如果想要使用state 中的数据,就必须要connect 进行一层包裹封装,换一句话来说就是必须要被connect 进行加强
- connect 就是方便我们组件能够获取到store 中的state
# 3.3 快速入门
1. 装包
npm i redux
npm i react-redux
2
2. 利用redux 来构建store
- 创建reducer/index.js 文件,构建reducer 来响应actions
- 床架store/index.js 文件,通过creatStore 方法,把我们的reducer 传进来
- 在app.js 中引入store
3. 引入Provider 组件
在app.js 中导如Provider 组件
import { Provider } from 'react-redux'
利用Provider 组件将我们整个结构进行包裹,并且传递store
function App() {
return (<Provider store={store}>...</Provider>)
}
2
3
只要我们把store 传递给了Provider 组件,那么Provider 组件就会在内部帮我们维护好store
4. connect 使用
导入connect 方法
import { connect } from 'react-redux'
调用connect 方法
connect(...)(Component)
connect 方法会有一个返回值,而这个返回值就是加强之后的组件
5. 关联store
在组件ComA 和ComB,分别导入connect 方法
利用connect 方法来对我们组件进行加强,并且导出
export default connect(map, mapDispatchToProps)(Component)
组件ComA 作为发送方,所以要实现第二个参数
...
const mapDispatchToProps = (dispatch) => {
return {
sendAction: () => {
dispatch({
type: 'add_action'
})
}
}
}
// A 是发送方,所以要实现connect 中的第二个参数
export default connect(null, mapDispatchToProps)(ComA)
2
3
4
5
6
7
8
9
10
11
12
13
组件ComB 作为接收方,所以要实现第一个参数
...
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps)(ComB)
2
3
4
5
6
6. 配置reducer
// 这个文件是创建reducer 函数的,专门用来处理发过来的action
const initState = {
count: 0
}
const reducer = (state = initState, action) => {
switch (action.type) {
case 'add_action':
return {
count: state.count + 1
}
default:
return state
}
}
module.exports = {
reducer
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 四、其他传值方式
# 4.1 HOC 高阶组件
HOC 高阶组件利用content 实现了数据穿越
任何想访问context里面的属性的组件都必须显示的指定一个contextTypes
的属性,如果没有指定该属性,那么组件通过this.context
访问属性将会出错。
getChildContext
与访问context属性需要通过contextTypes
指定可访问的属性一样,getChildContext
指定的传递给子组件的属性需要先通过childContextTypes
来执行,不然会报错。
// This code *does NOT work* becasue of a missing property from childContextTypes
var A = React.createClass({
childContextTypes: {//用于说明所传递的数据类型
// fruit is not specified, and so it will not be sent to the children of A
name: React.PropTypes.string.isRequired
},
getChildContext: function() {//getChildContext表示该组件通过context传递数据,该方法返回的对象就是context需要传递的数据
return {
name: "Jonas",
fruit: "Banana"
};
},
render: function() {
return <B />;
}
});
var B = React.createClass({
contextTypes: {//在子组件中用于说明context接收的数据类型
fruit: React.PropTypes.string.isRequired
},
render: function() {
return <div>My favorite fruit is: {this.context.fruit}</div>;
}
});
// Errors: Invariant Violation: A.getChildContext(): key "fruit" is not defined in childContextTypes.
React.render(<A />, document.body);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 4.2 pubsub-js
插件
利用pubsub-js
第三方插件可以实现各个组件之间相互传值,这里介绍他最简单的使用方法
装包
cnpm i pubsub-js -S
先在某一个组件中利用pubsub.publish()
发布一个事件,第一个参数是发布事件的名称(自定义),第二个参数是要传递的值
...
import pubsub from 'pubsub-js'
export default class Son extends Component {
...
pFunc = () => {
pubsub.publish('evt', '想传递的信息内容')
}
render() {
return (
<div>
<button onClick={() => { this.pFunc() }}>利用插件传值</button>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
事件就发布出去了,如果某个组件触发了将会自动接收,接收组件通过在constructor
中使用pubsub.subscribe()
方法来接收定义的内容并执行(这里不只为何会自动执行2次,好像是因为严格模式原因)
...
import pubsub from 'pubsub-js'
export default class Son2 extends Component {
...
constructor() {
super()
this.state = {
}
pubsub.subscribe('evt', (msg, data) => {
console.log(msg, data)
})
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17