﻿1
00:00:01,170 --> 00:00:03,570
‫Next up, let's write some code

2
00:00:03,570 --> 00:00:08,220
‫to detect a click that happens outside the modal window,

3
00:00:08,220 --> 00:00:12,513
‫and so when that happens, we then want to close the window.

4
00:00:13,980 --> 00:00:18,180
‫So basically, what I mean is that when this is open,

5
00:00:18,180 --> 00:00:20,220
‫and then when we click out here,

6
00:00:20,220 --> 00:00:22,710
‫that modal window should close,

7
00:00:22,710 --> 00:00:24,660
‫which is a pretty common pattern

8
00:00:24,660 --> 00:00:27,720
‫in websites and applications.

9
00:00:27,720 --> 00:00:30,600
‫So of course, right now, as I click here,

10
00:00:30,600 --> 00:00:32,070
‫that doesn't happen,

11
00:00:32,070 --> 00:00:35,040
‫and so what we want to do now, again,

12
00:00:35,040 --> 00:00:37,770
‫is to detect whenever a click happens

13
00:00:37,770 --> 00:00:42,540
‫outside of the modal window or one of its children,

14
00:00:42,540 --> 00:00:44,913
‫so basically, in this case, out here.

15
00:00:46,740 --> 00:00:51,300
‫All right, so let's come here to our window,

16
00:00:51,300 --> 00:00:56,133
‫and then add some global event listeners for a click event.

17
00:00:57,240 --> 00:01:00,840
‫So now, we will basically go back, so to say,

18
00:01:00,840 --> 00:01:03,933
‫to some more primitive DOM manipulation.

19
00:01:05,340 --> 00:01:09,400
‫So we need to do that inside a useEffect, right?

20
00:01:11,490 --> 00:01:15,810
‫And so let's specify our effect function,

21
00:01:15,810 --> 00:01:19,923
‫and as always, our dependency array.

22
00:01:22,230 --> 00:01:27,210
‫So again, here, we will now attach right to the document

23
00:01:27,210 --> 00:01:31,830
‫an event listener for any click events,

24
00:01:31,830 --> 00:01:36,063
‫and then here, we will have a handleClick callback function,

25
00:01:37,890 --> 00:01:41,160
‫and it needs to be in this external function,

26
00:01:41,160 --> 00:01:43,413
‫because we also need to remove this.

27
00:01:46,260 --> 00:01:51,260
‫So handleClick, which will receive the click event,

28
00:01:51,480 --> 00:01:53,970
‫and so as I was saying earlier,

29
00:01:53,970 --> 00:01:57,030
‫we also now need to remove this event listener

30
00:01:57,030 --> 00:01:58,860
‫as the component unmounts,

31
00:01:58,860 --> 00:02:02,130
‫and so for that, remember, we need to return

32
00:02:02,130 --> 00:02:03,453
‫a callback function,

33
00:02:04,800 --> 00:02:09,800
‫so we return document.removeEventListener,

34
00:02:12,689 --> 00:02:15,753
‫.click, and then that same handleClick.

35
00:02:16,837 --> 00:02:20,517
‫Now, here, apparently, we didn't include useEffect,

36
00:02:23,010 --> 00:02:27,660
‫or actually, we did, if we read the error message here,

37
00:02:27,660 --> 00:02:32,100
‫and so thankfully for us, we have this ESLint rule,

38
00:02:32,100 --> 00:02:35,670
‫which tells us that the React Hook, useEffect

39
00:02:35,670 --> 00:02:38,040
‫is called conditionally.

40
00:02:38,040 --> 00:02:41,730
‫So remember how we learned earlier, way back,

41
00:02:41,730 --> 00:02:44,310
‫that we are not allowed to do this,

42
00:02:44,310 --> 00:02:47,460
‫but if I didn't have this ESLint rule,

43
00:02:47,460 --> 00:02:50,100
‫I might actually have missed this,

44
00:02:50,100 --> 00:02:53,460
‫so then that would have created those kind of bugs

45
00:02:53,460 --> 00:02:55,053
‫that we talked about earlier,

46
00:02:55,980 --> 00:02:58,140
‫but now, that works, and so now,

47
00:02:58,140 --> 00:03:00,153
‫let's really think about this.

48
00:03:01,230 --> 00:03:04,290
‫So let's open up the window again,

49
00:03:04,290 --> 00:03:06,570
‫and as we just said earlier,

50
00:03:06,570 --> 00:03:11,570
‫we want something to happen whenever we click outside here.

51
00:03:11,970 --> 00:03:14,850
‫So basically, whenever the click event

52
00:03:14,850 --> 00:03:18,270
‫that we are handling here happens outside

53
00:03:18,270 --> 00:03:23,270
‫of this element right here, so outside of this StyledModal,

54
00:03:24,570 --> 00:03:28,863
‫so let's then manually select this element using a ref.

55
00:03:31,500 --> 00:03:36,500
‫So const ref will come from the useRef hook just like this,

56
00:03:42,300 --> 00:03:46,083
‫and now, we can add that here with the ref prop,

57
00:03:47,460 --> 00:03:51,480
‫and then we can use this selected element

58
00:03:51,480 --> 00:03:53,313
‫right here in our handler.

59
00:03:55,110 --> 00:03:59,133
‫So first of all, we need to check if that ref even exists,

60
00:04:00,120 --> 00:04:03,660
‫so for that, we can check ref.current,

61
00:04:03,660 --> 00:04:06,930
‫which is basically where the DOM node

62
00:04:06,930 --> 00:04:10,293
‫that references this element here will be stored.

63
00:04:11,130 --> 00:04:16,130
‫So if that exists, and now, comes basically the heart,

64
00:04:16,440 --> 00:04:18,873
‫or the solution of this whole thing.

65
00:04:19,710 --> 00:04:23,460
‫So I will just write it, and then I will explain it.

66
00:04:23,460 --> 00:04:28,460
‫So if the ref.current, so again,

67
00:04:28,980 --> 00:04:30,960
‫that's this window right there,

68
00:04:30,960 --> 00:04:35,960
‫so if that does not contain the element that was clicked,

69
00:04:37,290 --> 00:04:42,290
‫so e.target, then in that case, close the modal.

70
00:04:46,050 --> 00:04:47,640
‫Now, here, we are missing something,

71
00:04:47,640 --> 00:04:49,323
‫which is probably that ref,

72
00:04:50,430 --> 00:04:53,910
‫or actually, refs are stable,

73
00:04:53,910 --> 00:04:56,133
‫but the close function, we do need.

74
00:04:57,060 --> 00:04:59,730
‫So let's recap here.

75
00:04:59,730 --> 00:05:03,213
‫Maybe let's even add some console.log here first,

76
00:05:06,150 --> 00:05:08,343
‫so let's say, click outside,

77
00:05:09,810 --> 00:05:12,093
‫and so let's again analyze.

78
00:05:12,990 --> 00:05:17,990
‫So remember that ref.current is now this element right here,

79
00:05:18,360 --> 00:05:21,210
‫so this white modal window.

80
00:05:21,210 --> 00:05:23,130
‫So it's a DOM element right now,

81
00:05:23,130 --> 00:05:27,180
‫and on DOM elements, we can call the contain methods,

82
00:05:27,180 --> 00:05:29,520
‫which will basically return true

83
00:05:29,520 --> 00:05:32,850
‫whenever this element right here contains

84
00:05:32,850 --> 00:05:35,250
‫the element that we pass in,

85
00:05:35,250 --> 00:05:39,210
‫and so in this case, the element we pass in is e.target,

86
00:05:39,210 --> 00:05:42,273
‫which is simply the element where the click happened.

87
00:05:43,230 --> 00:05:47,400
‫So if the click happens on one of these elements right here,

88
00:05:47,400 --> 00:05:50,490
‫then that, of course, is inside the modal,

89
00:05:50,490 --> 00:05:54,030
‫and so then this whole thing here becomes false,

90
00:05:54,030 --> 00:05:56,010
‫and then nothing happens,

91
00:05:56,010 --> 00:05:58,530
‫but if a click happens out here,

92
00:05:58,530 --> 00:06:02,520
‫then that is no longer contained here in the ref,

93
00:06:02,520 --> 00:06:06,240
‫and so then this function here should get called,

94
00:06:06,240 --> 00:06:07,083
‫so let's see.

95
00:06:08,820 --> 00:06:11,100
‫So now, nothing is happening,

96
00:06:11,100 --> 00:06:13,620
‫and as I click, I get this error,

97
00:06:13,620 --> 00:06:15,993
‫and that's because it's called contains.

98
00:06:19,200 --> 00:06:21,180
‫All right, let's remove that.

99
00:06:21,180 --> 00:06:24,180
‫Let's click here, and then nothing happens,

100
00:06:24,180 --> 00:06:29,180
‫but let's try to click outside, and beautiful.

101
00:06:29,640 --> 00:06:34,640
‫So we get our log, and most importantly, the form closed,

102
00:06:34,830 --> 00:06:37,563
‫so the modal window was indeed closed.

103
00:06:38,790 --> 00:06:42,390
‫All right, now, there's just one problem with this,

104
00:06:42,390 --> 00:06:46,803
‫because watch what happens as I click here again.

105
00:06:47,820 --> 00:06:50,910
‫So that's very strange, right?

106
00:06:50,910 --> 00:06:55,910
‫So we get click outside, but the window doesn't show up,

107
00:06:56,610 --> 00:07:00,810
‫so what strange thing is happening here?

108
00:07:00,810 --> 00:07:03,840
‫Well, the reason for this is actually the way

109
00:07:03,840 --> 00:07:06,510
‫that events work in JavaScript,

110
00:07:06,510 --> 00:07:10,500
‫and in particular, the fact that events bubble up.

111
00:07:10,500 --> 00:07:13,680
‫So whenever I click here on this button,

112
00:07:13,680 --> 00:07:18,450
‫the modal window will be attached to the DOM, right?

113
00:07:18,450 --> 00:07:21,240
‫And it will be attached right here

114
00:07:21,240 --> 00:07:24,180
‫as a direct child of the body,

115
00:07:24,180 --> 00:07:27,570
‫and so if I click on the button,

116
00:07:27,570 --> 00:07:30,960
‫that event will bubble up all the way through the DOM

117
00:07:30,960 --> 00:07:34,530
‫until it also reaches that modal window,

118
00:07:34,530 --> 00:07:37,500
‫and so then the click is basically detected

119
00:07:37,500 --> 00:07:39,120
‫outside the modal window,

120
00:07:39,120 --> 00:07:42,513
‫which will immediately close that window again,

121
00:07:43,920 --> 00:07:47,340
‫so our logic is actually working just fine.

122
00:07:47,340 --> 00:07:50,640
‫So again, when we click here, the modal window

123
00:07:50,640 --> 00:07:54,240
‫basically gets opened for a millisecond,

124
00:07:54,240 --> 00:07:58,080
‫but then it immediately detects a click outside of it,

125
00:07:58,080 --> 00:08:01,170
‫and so then it will immediately close again,

126
00:08:01,170 --> 00:08:05,130
‫and so the way that we fix this is to not listen

127
00:08:05,130 --> 00:08:07,680
‫for these events on the bubbling phase,

128
00:08:07,680 --> 00:08:09,990
‫but on the capturing phase,

129
00:08:09,990 --> 00:08:14,370
‫so basically, as the event moves down the DOM tree

130
00:08:14,370 --> 00:08:16,830
‫and not up the DOM tree,

131
00:08:16,830 --> 00:08:19,740
‫and if all of this sounds a bit strange,

132
00:08:19,740 --> 00:08:23,760
‫then this is just the way that events work in JavaScript,

133
00:08:23,760 --> 00:08:25,860
‫and I have a whole lecture about this

134
00:08:25,860 --> 00:08:29,700
‫in the section on how React works behind the scenes,

135
00:08:29,700 --> 00:08:34,650
‫so please go back there if this is strange to you,

136
00:08:34,650 --> 00:08:38,520
‫but in any case, we can change this default behavior

137
00:08:38,520 --> 00:08:41,280
‫by here, passing in a third argument,

138
00:08:41,280 --> 00:08:44,640
‫which is simply to set this to true.

139
00:08:44,640 --> 00:08:49,500
‫And so if we use true here, then again,

140
00:08:49,500 --> 00:08:53,400
‫the event will actually be handled in the capturing phase,

141
00:08:53,400 --> 00:08:55,953
‫so as the event moves down the tree.

142
00:08:57,270 --> 00:08:59,433
‫So then here the same thing,

143
00:09:00,330 --> 00:09:01,773
‫let's just reload here,

144
00:09:02,910 --> 00:09:06,060
‫let's click, and nice.

145
00:09:06,060 --> 00:09:08,460
‫So that solves our problem,

146
00:09:08,460 --> 00:09:11,160
‫and so that's the reason why I told you earlier

147
00:09:11,160 --> 00:09:14,760
‫that even though we left vanilla JavaScript behind,

148
00:09:14,760 --> 00:09:17,130
‫it's still very important to understand

149
00:09:17,130 --> 00:09:18,930
‫how many things work,

150
00:09:18,930 --> 00:09:21,180
‫because without that knowledge,

151
00:09:21,180 --> 00:09:25,110
‫you probably wouldn't have been able to solve this issue,

152
00:09:25,110 --> 00:09:27,630
‫and so that fundamental knowledge

153
00:09:27,630 --> 00:09:29,643
‫is still extremely important.

154
00:09:31,200 --> 00:09:35,550
‫So let's just test if the click outside still works,

155
00:09:35,550 --> 00:09:38,850
‫and it does, great.

156
00:09:38,850 --> 00:09:43,353
‫So this is how any modern modal window is supposed to work.

157
00:09:44,910 --> 00:09:48,390
‫And now, just to finish, let's actually extract

158
00:09:48,390 --> 00:09:51,060
‫all of this here into a custom hook,

159
00:09:51,060 --> 00:09:53,880
‫which is a great exercise, not only

160
00:09:53,880 --> 00:09:56,130
‫to practice your React skills,

161
00:09:56,130 --> 00:09:59,730
‫but also to create a very nice and reusable

162
00:09:59,730 --> 00:10:01,113
‫custom hook for you.

163
00:10:02,190 --> 00:10:04,710
‫So if you want, you can pause the video here

164
00:10:04,710 --> 00:10:06,900
‫and do this extraction yourself,

165
00:10:06,900 --> 00:10:08,640
‫and then I will come back here

166
00:10:08,640 --> 00:10:10,563
‫once you are finished with that.

167
00:10:12,630 --> 00:10:16,050
‫All right, so hopefully, you tried this,

168
00:10:16,050 --> 00:10:20,490
‫but in any case, let's create that new file right here

169
00:10:20,490 --> 00:10:23,190
‫in the reusable Hooks folder,

170
00:10:23,190 --> 00:10:27,313
‫and let's call it useOutsideClick.js,

171
00:10:31,623 --> 00:10:34,517
‫and then a function called useOutsideClick.

172
00:10:38,910 --> 00:10:41,062
‫Let's also export this,

173
00:10:41,062 --> 00:10:45,360
‫and then what we need to do is to basically

174
00:10:45,360 --> 00:10:47,583
‫just grab all the code here.

175
00:10:48,483 --> 00:10:53,483
‫So I will just copy it for now, paste that here,

176
00:10:53,520 --> 00:10:57,753
‫and then of course, we need to get everything in here.

177
00:10:58,680 --> 00:11:03,680
‫So what do we need inside this custom hook from the outside?

178
00:11:04,200 --> 00:11:07,140
‫Well, it's actually already marked in red,

179
00:11:07,140 --> 00:11:10,353
‫which is basically this handler function right here.

180
00:11:11,490 --> 00:11:14,520
‫So when we use this custom hook now in our component,

181
00:11:14,520 --> 00:11:18,630
‫we will need to pass in that function,

182
00:11:18,630 --> 00:11:21,870
‫and so let's actually delete this now

183
00:11:21,870 --> 00:11:23,373
‫and call useOutsideClick.

184
00:11:26,550 --> 00:11:30,210
‫And so again, here, we now need to pass in that handler,

185
00:11:30,210 --> 00:11:34,593
‫and so that handler is going to be that close function.

186
00:11:36,270 --> 00:11:39,360
‫And so here, let's receive that,

187
00:11:39,360 --> 00:11:42,390
‫and then of course, here, we just call it handler,

188
00:11:42,390 --> 00:11:45,213
‫'cause this should become really generic.

189
00:11:47,430 --> 00:11:51,750
‫Then here, also, we need to use handler,

190
00:11:51,750 --> 00:11:55,590
‫and maybe to make this even a bit more flexible,

191
00:11:55,590 --> 00:11:58,980
‫let us here allow the user to specify

192
00:11:58,980 --> 00:12:01,350
‫whether they want to listen for the event

193
00:12:01,350 --> 00:12:04,083
‫in the bubbling or in the capturing phase.

194
00:12:05,130 --> 00:12:09,210
‫So let's call this one listenCapturing,

195
00:12:09,210 --> 00:12:13,050
‫and by default, let's set it to true,

196
00:12:13,050 --> 00:12:18,050
‫so then here, we will replace those with listenCapturing.

197
00:12:20,730 --> 00:12:24,750
‫Then we need that here like this,

198
00:12:24,750 --> 00:12:27,960
‫and now, let's see the problem that we have here,

199
00:12:27,960 --> 00:12:31,470
‫and yeah, the only thing that we are missing here

200
00:12:31,470 --> 00:12:32,880
‫is this ref,

201
00:12:32,880 --> 00:12:36,090
‫and so that means that this function here

202
00:12:36,090 --> 00:12:38,343
‫should probably return that ref.

203
00:12:39,600 --> 00:12:41,550
‫So let's store that here,

204
00:12:41,550 --> 00:12:44,703
‫and then all we need to do is to export that,

205
00:12:46,740 --> 00:12:48,843
‫so to return that actually from here,

206
00:12:50,100 --> 00:12:52,710
‫and with this, the same functionality

207
00:12:52,710 --> 00:12:57,030
‫should now be encapsulated here in this abstraction,

208
00:12:57,030 --> 00:13:01,770
‫so in that own custom hook right there.

209
00:13:01,770 --> 00:13:02,673
‫So let's see,

210
00:13:03,840 --> 00:13:05,940
‫and beautiful.

211
00:13:05,940 --> 00:13:10,773
‫Maybe just to finish, let's get rid of that console.log,

212
00:13:12,000 --> 00:13:15,240
‫and there we go, nice.

213
00:13:15,240 --> 00:13:19,110
‫And now, next up, let's also reuse this modal here

214
00:13:19,110 --> 00:13:20,970
‫for some other things.

215
00:13:20,970 --> 00:13:25,200
‫So this form right here, or actually, not this,

216
00:13:25,200 --> 00:13:29,250
‫but here, the edit form is still being rendered down here,

217
00:13:29,250 --> 00:13:32,940
‫and so let's also place that inside a modal window

218
00:13:32,940 --> 00:13:34,203
‫in the next lecture.

