Skip to content

Commit 805e7f8

Browse files
necolastrueadm
authored andcommitted
React events: add unmounting to Focus (#15396)
1 parent 543353a commit 805e7f8

File tree

1 file changed

+65
-15
lines changed

1 file changed

+65
-15
lines changed

packages/react-events/src/Focus.js

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type FocusProps = {
2222

2323
type FocusState = {
2424
isFocused: boolean,
25+
focusTarget: null | Element | Document,
2526
};
2627

2728
type FocusEventType = 'focus' | 'blur' | 'focuschange';
@@ -47,54 +48,87 @@ function createFocusEvent(
4748
}
4849

4950
function dispatchFocusInEvents(
50-
event: ReactResponderEvent,
51+
event: null | ReactResponderEvent,
5152
context: ReactResponderContext,
5253
props: FocusProps,
54+
state: FocusState,
5355
) {
54-
const {nativeEvent, target} = event;
55-
if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
56-
return;
56+
if (event != null) {
57+
const {nativeEvent} = event;
58+
if (
59+
context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
60+
) {
61+
return;
62+
}
5763
}
5864
if (props.onFocus) {
59-
const syntheticEvent = createFocusEvent('focus', target);
65+
const syntheticEvent = createFocusEvent(
66+
'focus',
67+
((state.focusTarget: any): Element | Document),
68+
);
6069
context.dispatchEvent(syntheticEvent, props.onFocus, {discrete: true});
6170
}
6271
if (props.onFocusChange) {
6372
const listener = () => {
6473
props.onFocusChange(true);
6574
};
66-
const syntheticEvent = createFocusEvent('focuschange', target);
75+
const syntheticEvent = createFocusEvent(
76+
'focuschange',
77+
((state.focusTarget: any): Element | Document),
78+
);
6779
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
6880
}
6981
}
7082

7183
function dispatchFocusOutEvents(
72-
event: ReactResponderEvent,
84+
event: null | ReactResponderEvent,
7385
context: ReactResponderContext,
7486
props: FocusProps,
87+
state: FocusState,
7588
) {
76-
const {nativeEvent, target} = event;
77-
if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
78-
return;
89+
if (event != null) {
90+
const {nativeEvent} = event;
91+
if (
92+
context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
93+
) {
94+
return;
95+
}
7996
}
8097
if (props.onBlur) {
81-
const syntheticEvent = createFocusEvent('blur', target);
98+
const syntheticEvent = createFocusEvent(
99+
'blur',
100+
((state.focusTarget: any): Element | Document),
101+
);
82102
context.dispatchEvent(syntheticEvent, props.onBlur, {discrete: true});
83103
}
84104
if (props.onFocusChange) {
85105
const listener = () => {
86106
props.onFocusChange(false);
87107
};
88-
const syntheticEvent = createFocusEvent('focuschange', target);
108+
const syntheticEvent = createFocusEvent(
109+
'focuschange',
110+
((state.focusTarget: any): Element | Document),
111+
);
89112
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
90113
}
91114
}
92115

116+
function unmountResponder(
117+
context: ReactResponderContext,
118+
props: FocusProps,
119+
state: FocusState,
120+
): void {
121+
if (state.isFocused) {
122+
dispatchFocusOutEvents(null, context, props, state);
123+
}
124+
}
125+
93126
const FocusResponder = {
94127
targetEventTypes,
95128
createInitialState(): FocusState {
96129
return {
97130
isFocused: false,
131+
focusTarget: null,
98132
};
99133
},
100134
onEvent(
@@ -103,25 +137,41 @@ const FocusResponder = {
103137
props: Object,
104138
state: FocusState,
105139
): void {
106-
const {type} = event;
140+
const {type, target} = event;
107141

108142
switch (type) {
109143
case 'focus': {
110144
if (!state.isFocused && !context.hasOwnership()) {
111-
dispatchFocusInEvents(event, context, props);
145+
state.focusTarget = target;
146+
dispatchFocusInEvents(event, context, props, state);
112147
state.isFocused = true;
113148
}
114149
break;
115150
}
116151
case 'blur': {
117152
if (state.isFocused) {
118-
dispatchFocusOutEvents(event, context, props);
153+
dispatchFocusOutEvents(event, context, props, state);
119154
state.isFocused = false;
155+
state.focusTarget = null;
120156
}
121157
break;
122158
}
123159
}
124160
},
161+
onUnmount(
162+
context: ReactResponderContext,
163+
props: FocusProps,
164+
state: FocusState,
165+
) {
166+
unmountResponder(context, props, state);
167+
},
168+
onOwnershipChange(
169+
context: ReactResponderContext,
170+
props: FocusProps,
171+
state: FocusState,
172+
) {
173+
unmountResponder(context, props, state);
174+
},
125175
};
126176

127177
export default {

0 commit comments

Comments
 (0)