# Ⅸ-组件间通信示例

# 1、兄弟间传值方式1 --> [子传父、父传子]

  1. 父组件定义一个[状态]或[方法],其中[方法]能修改[状态]
  • 将[方法]传给要进行发送值的子组件,通过继承到的[方法],去修改父组件的[状态]
  • 将[状态]传给要接受值的子组件,这样就能做到兄弟间传值
  1. 代码示例
----------父组件------------------------
class Main extends React.Component<IProps> {
constructor(props) {
  super(props);
  this.state = { searchParams: {} };
}
handleIPSearch = (params) => {
  this.setState({ searchParams: params });
};
render() {
  <子组件1:要对组件2进行修改的 handleIPSearch={handleIPSearch}  />
  <子组件2:要接受值的    searchParams={this.state.searchParams}/>
}
}
--------------子组件1----------------------
const ManageTable = (props: IProps) => {
const {  handleIPSearch } = props;
return(
   //此处即可调用修改父组件状态的函数
  <a onClick={() => {   handleIPSearch(data) }} >
  对父组件值进行修改,间接改变组件2接收到的值
</a>)
}
--------------子组件2----------------------
const IPInfo: FC = (props) => {
  //此处就能使用父组件的状态
const { searchParams } = props;
  return( <span>searchParams</span> )    
}
1
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

# 2、兄弟间传值方式2 -->[mitt(发布订阅者模式)]

此方法用的是[mitt]实现,其实本质上就是注册一个全局变量进行监听 --> mitt源码地址 (opens new window)

可以自己实现,此处因为人家写的不错了,就以此作为例子

  1. 安装或者直接复制使用
npm install --save mitt
1
  1. 使用示例
-------------- 首先要定义一个公用全局变量  --------------------------
//文件 utils/index.ts
import mitt from './mitt';
//此处声明,将其变为全局变量
const eventBus = mitt();
export { eventBus }
---------------- 发送值的组件(要修改别人的组件)  ---------------------
//导入共有变量
import { eventBus } from '~/utils';
  <a
  onClick={() => {
	//延迟发送是本人此之前有一个跳转动作,跳转到接收方组件
    // 防止修改了值的时候但是接收组件未注册  正常情况直接发送即可     
    //setTimeout(() => {
    // eventBus.emit('foo', data);
    //}, 100);
    eventBus.emit('foo', data);    
   }}
  />;

------------------ 接受方组件(接受发送方的组件)  -------------------------------------

const Search: FC<IProps> = (props) => {
  useEffect(() => {
    //替换为mitt写法,此时已经接收到了
    eventBus.on('foo', (searchParams) => {console.log('接受到值了',searchParams) }
    });
  }, []);
} 
1
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
  1. mitt源码
export type EventType = string | symbol;

// An event handler can take an optional event argument
// and should not return a value
export type Handler<T = unknown> = (event: T) => void;
export type WildcardHandler<T = Record<string, unknown>> = (
  type: keyof T,
  event: T[keyof T]
) => void;

// An array of all currently registered event handlers for a type
export type EventHandlerList<T = unknown> = Array<Handler<T>>;
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<
  WildcardHandler<T>
>;

// A map of event types and their corresponding event handlers.
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
  keyof Events | '*',
  EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>
>;

export interface Emitter<Events extends Record<EventType, unknown>> {
  all: EventHandlerMap<Events>;

  on: (<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) => void) & ((type: '*', handler: WildcardHandler<Events>) => void);

  off: (<Key extends keyof Events>(
    type: Key,
    handler?: Handler<Events[Key]>
  ) => void) & ((type: '*', handler: WildcardHandler<Events>) => void);

  emit: (<Key extends keyof Events>(type: Key, event: Events[Key]) => void) & (<Key extends keyof Events>(
    type: undefined extends Events[Key] ? Key : never
  ) => void);
}

/**
 * Mitt: Tiny (~200b) functional event emitter / pubsub.
 * @name mitt
 * @returns {Mitt}
 */
export default function mitt<Events extends Record<EventType, unknown>>(
  all?: EventHandlerMap<Events>
): Emitter<Events> {
  type GenericEventHandler =
    | Handler<Events[keyof Events]>
    | WildcardHandler<Events>;
  all = all || new Map();

  return {
    /**
     * A Map of event names to registered handler functions.
     */
    all,

    /**
     * Register an event handler for the given type.
     * @param {string|symbol} type Type of event to listen for, or `'*'` for all events
     * @param {Function} handler Function to call in response to given event
     * @memberOf mitt
     */
    on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
      const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
      if (handlers) {
        handlers.push(handler);
      } else {
        all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
      }
    },

    /**
     * Remove an event handler for the given type.
     * If `handler` is omitted, all handlers of the given type are removed.
     * @param {string|symbol} type Type of event to unregister `handler` from, or `'*'`
     * @param {Function} [handler] Handler function to remove
     * @memberOf mitt
     */
    off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
      const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
      if (handlers) {
        if (handler) {
          handlers.splice(handlers.indexOf(handler) >>> 0, 1);
        } else {
          all!.set(type, []);
        }
      }
    },

    /**
     * Invoke all handlers for the given type.
     * If present, `'*'` handlers are invoked after type-matched handlers.
     *
     * Note: Manually firing '*' handlers is not supported.
     *
     * @param {string|symbol} type The event type to invoke
     * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
     * @memberOf mitt
     */
    emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
      let handlers = all!.get(type);
      if (handlers) {
        (handlers as EventHandlerList<Events[keyof Events]>)
          .slice()
          .map((handler) => {
            handler(evt!);
          });
      }

      handlers = all!.get('*');
      if (handlers) {
        (handlers as WildCardEventHandlerList<Events>)
          .slice()
          .map((handler) => {
            handler(type, evt!);
          });
      }
    },
  };
}
1
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

# 3、父组件调用子组件方法

# ① 场景描述

当本人要封装一个关于绑定手机号的组件并应用于项目中, 但是到整体表单校验时 (我需要知道手机号列是否进行了修改),以此来判断是否发送修改请求

因为 [手机列] 的相关校验写在封装的方法中,在父组件需要调用一次其校验方式,得到其校验结果

image-20210820184059775

# ② 使用hooks的-- useImperativeHandle (opens new window)useRef (opens new window)

//父组件代码
import React, { FC, useEffect, useRef } from 'react';
import MyPhoneInput from '~/components/my-phone-input'; //引入子组件
const ChannelEdit: FC<IProps> = (props) => {
const childRef = useRef(); //useRef
const checkSubmit = () => childRef?.current?.checkSubmit(); //调用子组件的方法
return(
<MyPhoneInput  cRef={childRef} />  
)    
}
1
2
3
4
5
6
7
8
9
10
//子组件代码
import React, { useEffect, useState, useImperativeHandle } from 'react';
const SuperPhoneInput = (props) => {
useImperativeHandle(cRef, () => ({
checkSubmit,
}));
//需要在父组件调用的 子组件方法 
const checkSubmit = () => {
let tels = [];
let ischange = false;
	//..... 各种操作,然后返回结果给父组件
return { ischange, tels };
};  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Last Updated: 2/13/2023, 9:16:34 PM