1、父组件向子组件传值
父组件向子组件传值一般采用props属性传递
父组件:
import React from 'react'
import Child from './Child'
const dataList = [
{ id: '001', value: '张三' },
{ id: '002', value: '李四' }
]
const Parent = props => {
return (
<ul>
<Child dataList={dataList}></Child>
</ul>
)
}
export default Parent
子组件:
import React from 'react'
const Child = props => {
return (
<React.Fragment>
{
props.dataList.map(item => <li key={item.id}>{item.value}</li>)
}
</React.Fragment>
)
}
export default Child
2、子组件向父组件传值
子组件调用父组件传过来的回调函数来更改父组件的state
父组件
import React, { useState } from 'react'
import Child from './Child'
const Parent = props => {
const [count, setCount] = useState(0)
return (
<Child count={count} setCount={setCount}></Child>
)
}
export default Parent
子组件
import React from 'react'
const Child = props => {
return (
<React.Fragment>
<button onClick={() => {props.setCount(props.count-1)}}>-</button>
<span>{props.count}</span>
<button onClick={() => {props.setCount(props.count+1)}}>+</button>
</React.Fragment>
)
}
export default Child
3、嵌套组件通讯(祖孙组件)
context是一个全局变量,像是一个大容器,在任何地方都可以访问到,我们可以把要通信的信息放在context上,然后在其他组件中可以随意取到;
但是React官方不建议使用大量context,尽管他可以减少逐层传递,但是当组件结构复杂的时候,我们并不知道context是从哪里传过来的;而且context是一个全局变量,全局变量正是导致应用走向混乱的罪魁祸首。
context.js存放上下文
import React from 'react'
const MyContext = React.createContext()
export default MyContext
父组件
import React, { useState } from 'react'
import Child from './Child'
import MyContext from './context'
const Parent = props => {
return (
<MyContext.Provider value="world">
<Child />
</MyContext.Provider>
)
}
export default Parent
子组件
import React from 'react'
import Son from './Son'
const Child = props => {
return (
<Son />
)
}
export default Child
孙组件
import React from 'react'
import MyContext from './context'
const Son = props => {
return (
<MyContext.Consumer>
{
context => <div>{context}</div>
}
</MyContext.Consumer>
)
}
export default Son
可以使用React.useContext钩子获取上下文对象绑定的值
import React from 'react'
import MyContext from './context'
const Son = props => {
const context = React.useContext(MyContext);
return <div>{context}</div>
}
export default Son
4、非嵌套组件通讯
非嵌套组件: 就是没有任何包含关系的组件,包括兄弟组件以及不再同一个父级的非兄弟组件。
使用事件订阅,即一个发布者,一个或多个订阅者。
4.1、使用event模块或其他模块发布订阅
4.1.1、event模块使用
安装event模块
cnpm i -S event
新建event.js文件
import { EventEmitter } from 'events'
export default new EventEmitter()
Brother组件
import React from 'react'
import emitter from './events'
const Brother = props => {
const handle = () => {
emitter.emit('greet', 'sister, hello.')
}
return <button onClick={handle}>向sister问候</button>
}
export default Brother
Sister组件
import React from 'react'
import emitter from './events'
const Sister = props => {
const [msg, setMsg] = React.useState('');
React.useEffect(() => {
emitter.on('greet', msg => {setMsg(msg)})
return () => {
emitter.removeListener('greet', msg => {setMsg(msg)})
};
});
return <div>{msg}</div>
}
export default Sister
4.1.2、event模块原理
看看event模块是怎样实现发布、订阅、注销事件的。event模块是基于观察者模式实现的,实例对象中有一个_event对象(Map结构)管理注册的事件,发布、订阅、注销也就是对该对象进行添加、删除、调用操作。
初始化
// 对象的构造函数
function EventEmitter() {
//这个就是用于调用Init函数
// 也就是说说构造函数,仅仅是调用的下面的Init
EventEmitter.init.call(this);
}
// 导出函数,这个就是一个events模块的总体导出函数
// 在上述的用法中,我们都是需要创建一个EventEmitter对象的
module.exports = EventEmitter;
// 用于兼容node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
// 是否使用domain,默认用法是不使用。Domain其实是EventEmitter子类,
// 一个单独的模块,这里不进一步分析,有兴趣可以看domain.js
EventEmitter.usingDomains = false;
// 同上,用于domain模块
EventEmitter.prototype.domain = undefined;
// 这个就是用于存储事件和回调的类map对象
EventEmitter.prototype._events = undefined;
//
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners // are added to it. This is a useful default which helps finding memory leaks.
// 默认的最大的观察者的个数,默认为10, 如果超过,会有警告信息,以避免内存泄漏
EventEmitter.defaultMaxListeners = 10;
// 初始化,构造函数必须调用的部分
EventEmitter.init = function() {
// 下面是对domain的处理,不考虑
this.domain = null;
if (EventEmitter.usingDomains) {
// if there is an active domain, then attach to it.
domain = domain || require('domain');
if (domain.active && !(this instanceof domain.Domain)) {
this.domain = domain.active;
}
}
// 创建了一个_events 的空对象,相当于创建了一个map
if (!this._events || this._events === Object.getPrototypeOf(this)._events)
this._events = {};
// 用于保存当前最大监听数目,后面会用到
this._maxListeners = this._maxListeners || undefined;
};
事件订阅
EventEmitter.prototype.addListener = function addListener(type, listener) {
var m;
// 首先查看的是否listener为一个函数,确保是可以被执行的会掉函数
if (!util.isFunction(listener))
throw TypeError('listener must be a function');
//确保_events对象已经被创建
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
// 从注释上,这个是防止递归调用的,这里只有定义了newListener后,才会发送事件
// newListener,从本函数的代码,可以看出,newListener没有被定义,可以忽视
if (this._events.newListener)
this.emit('newListener', type,
util.isFunction(listener.listener) ?
listener.listener : listener);
// 查看该type(事件)是否存在,
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
// 如果不存在,直接存入就可以
this._events[type] = listener;
else if (util.isObject(this._events[type]))
// If we've already got an array, just append.
// 如果存在,并且是array,直接push
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
// 如果不是array,生成一个新的array
this._events[type] = [this._events[type], listener];
// Check for listener leak
// 下面的代码就是查看是否监听者超过了最大的数目,这个是关于默认的数目的
if (util.isObject(this._events[type]) && !this._events[type].warned) {
var m;
if (!util.isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
//如果数目过大,直接给出console.error
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d %s listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length, type);
console.trace();
}
}
return this;
};
// 这里就是说on 和addListener是相互一样的,别名。
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
// 查看once,事件触发一次回调函数,就删除,相当于调用了removeListener
EventEmitter.prototype.once = function once(type, listener) {
// 依然一样是确保参数为函数,可以回调
if (!util.isFunction(listener))
throw TypeError('listener must be a function');
// 回调函数是否被fire了
var fired = false;
// 帮助函数
function g() {
// 删除回调函数,请注意,一旦被fired掉,就删除
this.removeListener(type, g);
// 检查是否被fired过了,我想,这个可能是防止重复增加的case,也就是多次调用了once的 情况
if (!fired) {
fired = true;
// 执行回调函数
listener.apply(this, arguments);
}
}
// 增加一个listener属性为回调函数
g.listener = listener;
// 增加具体的回调函数,该回调函数变成了帮助函数g,而不是listener
this.on(type, g);
// 返回整个对象
return this;
};
事件发布
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, listeners;
// 还是检查当前存储的events队列是否为空
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
// 这里需要对error进行特殊处理,如果没有error事件的监听者,直接会抛出error的错误,
// 所有对error的时间的处理要特别注意。
// 也就是所emit(‘error’)有抛出异常的功能,这个是文档中没有的
if (type === 'error' && !this._events.error) {
er = arguments[1];
// 这个是具体domain的处理
if (this.domain) {
if (!er)
er = new Error('Uncaught, unspecified "error" event.');
er.domainEmitter = this;
er.domain = this.domain;
er.domainThrown = false;
this.domain.emit('error', er);
} else if (er instanceof Error) {
// 直接抛出异常,如果emit一个参数Error的实例
throw er; // Unhandled 'error' event
} else {
抛出生成的异常
throw Error('Uncaught, unspecified "error" event.');
}
return false;
}
// 找到对应事件的回调函数
handler = this._events[type];
// 如果回调函数没有定义,说明不存在,直接返回false
if (util.isUndefined(handler))
return false;
// 如果是domain的使用,直接进入domain内处理
if (this.domain && this !== process)
this.domain.enter();
//处理是单个的函数的情况。
if (util.isFunction(handler)) {
// 检查参数的情况,注意,至少有事件这一个argument。所以先处理1,2,3
switch (arguments.length) {
// fast cases
case 1:
// 当回调函数没有参数时候
handler.call(this);
break;
case 2:
// 当回调函数有一个参数时候
handler.call(this, arguments[1]);
break;
case 3:
// 当回调函数有二个参数时候
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
// 当回调函数三个或者以上参数时候,就会做一个copy,然后再调用
// 所以,这里我们可以特别注意的地方是,为了效率考虑,回调函数最好不要用3个或者3个以上的函数参数
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
handler.apply(this, args);
}
} // 这里处理回调函数为一个数组的情况
else if (util.isObject(handler)) {
// 这里直接生成一个参数的拷贝
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
// 生成一个回调函数的新队列
listeners = handler.slice();
len = listeners.length;
// 逐个调用回调函数
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
// 处理domain 的情况,直接退出
if (this.domain && this !== process)
this.domain.exit();
//返回true,有回调函数处理的情况
return true;
};
事件注销
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, position, length, i;
//依然是检查listener是否为函数
if (!util.isFunction(listener))
throw TypeError('listener must be a function');
// 确保_events是否为空,以及事件存在在对象中
if (!this._events || !this._events[type])
return this;
// 等到回调函数的value
list = this._events[type];
length = list.length;
position = -1;
// 如果当前的value和要删除的回调是相等的,包含once的内容
if (list === listener ||
(util.isFunction(list.listener) && list.listener === listener)) {
// 直接删除
delete this._events[type];
// 发送事件
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} // 如果是队列,直接出来
else if (util.isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
// 如果查找不到,直接返回
if (position < 0)
return this;
// 如果长度为1,说明可以删除
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
// 直接在数组中删除该回调
list.splice(position, 1);
}
// fire removeListener事件
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
// 删除与单一事件相关的所有回调函数
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var key, listeners;
// 确保_events不为空
if (!this._events)
return this;
// not listening for removeListener, no need to emit
// 查看当前是否有removeListener的监听者,从本模块看,没有赋值,所以一般情况下,都是
// 直接删除回调然后返回
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
// 处理没有参数的情况,在没有参数的情况下,就是删除所有事件的回调
// 相当于清空。
if (arguments.length === 0) {
for (key in this._events) {
// 注意有特殊情况是,不删除removeListener的回调
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
// 处理有具体参数的情况, 找到具体回调函数
listeners = this._events[type];
// 如果回调是单个函数,直接删除就好
if (util.isFunction(listeners)) {
this.removeListener(type, listeners);
} else if (Array.isArray(listeners)) {
// LIFO order
// 处理回调函数是一个数组的情况,从后往前一个一个删除。
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
// 清空事件对于的回调函数对象
delete this._events[type];
return this;
};
4.2、使用react-redux进行全局状态管理
这里仅仅以redux为例进行状态管理,使用其他状态管理工具也可以,比如mobx。
Action
export const setMsg = msg => ({
type: 'SET_MSG',
msg
})
reducer
const reducer = (state, action) => {
switch (action.type) {
case 'SET_MSG':
return {
...state,
msg: action.msg
}
default:
return state
}
}
export default reducer
App
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import reducer from '../reducers'
import BrotherContaniner from '../containers/BrotherContainer'
import SisterContainer from '../containers/SisterContainer'
const store = createStore(reducer, {msg: ''})
render(
<Provider store={store}>
<BrotherContaniner />
<SisterContainer />
</Provider>,
document.getElementById('app')
)
BrotherContainer
import { connect } from 'react-redux'
import Brother from '../module/Brother'
import { setMsg } from '../acions'
const mapDispatchToProps = dispatch => ({
setMsg: msg => dispatch(setMsg(msg))
})
export default connect(null, mapDispatchToProps)(Brother)
Brother
import React from 'react'
const Brother = props => {
const handle = () => {
props.setMsg('hello, sister.')
}
return <button onClick={handle}>向sister问候</button>
}
export default Brother
SisterContainer
import { connect } from 'react-redux'
import Sister from '../module/Sister'
const mapStateToProps = state => ({
msg: state.msg
})
export default connect(mapStateToProps, null)(Sister)
Sister
import React from 'react'
const Sister = props => {
return <div>{props.msg}</div>
}
export default Sister
参考:
https://blog.csdn.net/xingfuz...
https://www.cnblogs.com/qinne...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。