1
00:00:00,000 --> 00:00:03,000
So I got this IMAGE PICKER placeholder here,

2
00:00:03,000 --> 00:00:07,000
and my idea here simply is to display an image picker

3
00:00:07,000 --> 00:00:11,000
that can be used by the user to attach an image to the form

4
00:00:11,000 --> 00:00:15,000
and to upload an image when the form is submitted.

5
00:00:15,000 --> 00:00:18,000
Now for that, I'll actually build a separate component

6
00:00:18,000 --> 00:00:21,000
since this image picker is a bit more complex,

7
00:00:21,000 --> 00:00:24,000
and hence, I'll go to this components folder,

8
00:00:24,000 --> 00:00:26,000
and then there in the meals folder,

9
00:00:26,000 --> 00:00:29,000
I'll add my image-picker.js file.

10
00:00:29,000 --> 00:00:32,000
We could also add it in any other sub folder here,

11
00:00:32,000 --> 00:00:34,000
but since I'm only using it in this

12
00:00:34,000 --> 00:00:36,000
meal-related form, I'll add it here.

13
00:00:38,000 --> 00:00:41,000
Attached you also find an image-picker.module.css file

14
00:00:41,000 --> 00:00:43,000
with some styles we'll use.

15
00:00:45,000 --> 00:00:50,000
And then here we can and should export a component function

16
00:00:51,000 --> 00:00:56,000
that can be called ImagePicker like this.

17
00:00:56,000 --> 00:00:58,000
And the job of this component function

18
00:00:58,000 --> 00:01:00,000
is to output some markup

19
00:01:00,000 --> 00:01:04,000
and then also handle the picking process.

20
00:01:05,000 --> 00:01:09,000
Now for the markup, I'll start with a div here

21
00:01:09,000 --> 00:01:13,000
with a class name of classes.picker.

22
00:01:13,000 --> 00:01:15,000
And for classes to be available,

23
00:01:15,000 --> 00:01:18,000
we should of course import classes

24
00:01:18,000 --> 00:01:23,000
from ./image-picker.module.css.

25
00:01:25,000 --> 00:01:28,000
And then in that div, I wanna start by adding a label,

26
00:01:30,000 --> 00:01:34,000
a label with a configurable text, which is expected

27
00:01:34,000 --> 00:01:36,000
to be received via props.

28
00:01:36,000 --> 00:01:39,000
So I'll add the label prop here

29
00:01:39,000 --> 00:01:42,000
and then use that here for outputting that label text.

30
00:01:44,000 --> 00:01:46,000
Now I will add the htmlFor prop here

31
00:01:46,000 --> 00:01:49,000
to connect this label to some input,

32
00:01:49,000 --> 00:01:51,000
and I'll connect it to an input with a name

33
00:01:51,000 --> 00:01:55,000
of image or with an ID of image to be precise.

34
00:01:56,000 --> 00:01:59,000
And therefore we of course also need such an input.

35
00:01:59,000 --> 00:02:02,000
But I will nest that input into a div here,

36
00:02:02,000 --> 00:02:07,000
a div to which I assign a className of controls.

37
00:02:08,000 --> 00:02:11,000
And then in there I wanna have an input element

38
00:02:12,000 --> 00:02:14,000
where the type should be set to file,

39
00:02:14,000 --> 00:02:16,000
because this should be an input

40
00:02:16,000 --> 00:02:18,000
that allows us to select a file.

41
00:02:19,000 --> 00:02:21,000
Now the ID here should be image

42
00:02:21,000 --> 00:02:24,000
so that we connect this label to this input.

43
00:02:26,000 --> 00:02:29,000
And I'll add the accept prop

44
00:02:29,000 --> 00:02:31,000
to control which files are accepted.

45
00:02:31,000 --> 00:02:34,000
And here I only wanna accept files that are

46
00:02:34,000 --> 00:02:37,000
of type image/png or image/jpeg,

47
00:02:37,000 --> 00:02:40,000
so no other files can be uploaded

48
00:02:40,000 --> 00:02:41,000
with help of that picker.

49
00:02:42,000 --> 00:02:46,000
I'll also give this input a name of image,

50
00:02:46,000 --> 00:02:49,000
which will later be important for extracting

51
00:02:49,000 --> 00:02:50,000
that uploaded image.

52
00:02:52,000 --> 00:02:54,000
Alternatively, we can also make this image picker

53
00:02:54,000 --> 00:02:57,000
a bit more configurable by accepting that name

54
00:02:57,000 --> 00:03:00,000
as a prop and then setting this like this.

55
00:03:02,000 --> 00:03:06,000
We could now also use this name as a value for the ID

56
00:03:06,000 --> 00:03:08,000
to make this even more configurable.

57
00:03:08,000 --> 00:03:10,000
And I will do that here.

58
00:03:10,000 --> 00:03:13,000
Also using it here for htmlFor.

59
00:03:16,000 --> 00:03:20,000
Now with that, we got a very basic unfinished version,

60
00:03:20,000 --> 00:03:24,000
but we can now use this image picker in our form.

61
00:03:24,000 --> 00:03:27,000
So back in page.js, in this form here

62
00:03:27,000 --> 00:03:29,000
where I have this image picker,

63
00:03:29,000 --> 00:03:31,000
we can output the ImagePicker

64
00:03:32,000 --> 00:03:35,000
using our own custom ImagePicker component.

65
00:03:36,000 --> 00:03:38,000
Of course, for that, you must import it

66
00:03:38,000 --> 00:03:40,000
from that components folder.

67
00:03:42,000 --> 00:03:45,000
And with that added, you will see that image picker here.

68
00:03:47,000 --> 00:03:49,000
Now this would work.

69
00:03:49,000 --> 00:03:52,000
You could now click it and then select images.

70
00:03:52,000 --> 00:03:55,000
But I actually wanna make this image picker

71
00:03:55,000 --> 00:03:56,000
a bit more fancy.

72
00:03:56,000 --> 00:04:00,000
I wanna make sure that it also shows a preview

73
00:04:00,000 --> 00:04:01,000
of the picked image.

74
00:04:01,000 --> 00:04:04,000
And I wanna get rid of this ugly button here,

75
00:04:04,000 --> 00:04:07,000
and instead display my own button

76
00:04:07,000 --> 00:04:09,000
over which I have full control.

77
00:04:10,000 --> 00:04:14,000
And therefore, back here in the image-picker.js file,

78
00:04:14,000 --> 00:04:19,000
we can start by adding a class here to this input,

79
00:04:19,000 --> 00:04:21,000
and that should be the input class.

80
00:04:22,000 --> 00:04:24,000
And this class will essentially make sure

81
00:04:24,000 --> 00:04:27,000
that the input itself is hidden.

82
00:04:27,000 --> 00:04:30,000
So now we can't see it anymore down there

83
00:04:30,000 --> 00:04:33,000
because I instead now wanna render my own button.

84
00:04:34,000 --> 00:04:37,000
So therefore here, maybe after the input,

85
00:04:37,000 --> 00:04:38,000
we can output a button,

86
00:04:39,000 --> 00:04:43,000
which should receive a class of button

87
00:04:45,000 --> 00:04:49,000
and which should be of type button, which is important

88
00:04:49,000 --> 00:04:53,000
so that it won't submit the surrounding form.

89
00:04:53,000 --> 00:04:55,000
If you would not set the type,

90
00:04:55,000 --> 00:04:57,000
it would by default be type submit,

91
00:04:57,000 --> 00:05:00,000
and it would submit the surrounding form,

92
00:05:00,000 --> 00:05:03,000
which I don't want to happen here.

93
00:05:03,000 --> 00:05:04,000
So the type should be button.

94
00:05:06,000 --> 00:05:07,000
And then we can set a text

95
00:05:07,000 --> 00:05:10,000
of pick an image here on that button.

96
00:05:12,000 --> 00:05:15,000
Now with that, we got this button here, which is great,

97
00:05:15,000 --> 00:05:18,000
but of course the button isn't doing anything.

98
00:05:18,000 --> 00:05:23,000
And what I want the button to do is click this input,

99
00:05:23,000 --> 00:05:26,000
which is not visible anymore for me.

100
00:05:26,000 --> 00:05:28,000
It's still part of the DOM.

101
00:05:28,000 --> 00:05:29,000
It's just not visible

102
00:05:29,000 --> 00:05:32,000
because of the CSS styles I applied

103
00:05:32,000 --> 00:05:34,000
and now I wanna use my own button

104
00:05:34,000 --> 00:05:37,000
to click this input under the hood so that I still

105
00:05:37,000 --> 00:05:40,000
leverage this input, but I don't show it

106
00:05:40,000 --> 00:05:42,000
because it's not looking that nice.

107
00:05:43,000 --> 00:05:46,000
Now to make that work, we of course need

108
00:05:46,000 --> 00:05:48,000
to handle clicks on this button

109
00:05:48,000 --> 00:05:52,000
and then forward them, so to say, to this input.

110
00:05:53,000 --> 00:05:56,000
Now we can handle clicks to this button by adding a

111
00:05:56,000 --> 00:06:00,000
function here to our image picker, like handlePickClick

112
00:06:02,000 --> 00:06:03,000
or anything like that.

113
00:06:05,000 --> 00:06:08,000
And now we can assign this function as a value

114
00:06:08,000 --> 00:06:11,000
for the onClick prop here.

115
00:06:14,000 --> 00:06:17,000
Now if you do that, you'll be greeted by an error

116
00:06:17,000 --> 00:06:20,000
because as I briefly mentioned earlier,

117
00:06:20,000 --> 00:06:22,000
event handlers like this,

118
00:06:22,000 --> 00:06:24,000
so whenever you are assigning a function

119
00:06:24,000 --> 00:06:27,000
or any value to the onClick prop

120
00:06:27,000 --> 00:06:30,000
or any other event handling prop cannot be used

121
00:06:30,000 --> 00:06:34,000
in server components, which kind of makes sense

122
00:06:34,000 --> 00:06:36,000
because those interactions happen on the client

123
00:06:36,000 --> 00:06:38,000
in the browser.

124
00:06:38,000 --> 00:06:41,000
The user clicks on the button in the browser.

125
00:06:42,000 --> 00:06:45,000
Therefore, we must mark this ImagePicker

126
00:06:45,000 --> 00:06:46,000
as a client component

127
00:06:46,000 --> 00:06:49,000
by adding this use client directive

128
00:06:49,000 --> 00:06:50,000
at the top of this file.

129
00:06:52,000 --> 00:06:54,000
With that done, the error goes away,

130
00:06:55,000 --> 00:06:57,000
but of course, we're not doing anything

131
00:06:57,000 --> 00:06:59,000
when the user clicks the button.

132
00:06:59,000 --> 00:07:03,000
What I wanna do is, as mentioned, forward that click.

133
00:07:03,000 --> 00:07:05,000
I wanna trigger a click on this input,

134
00:07:05,000 --> 00:07:09,000
and we can do that with help of refs,

135
00:07:09,000 --> 00:07:11,000
a feature built into React.

136
00:07:11,000 --> 00:07:14,000
We can create a ref with the useRef hook,

137
00:07:15,000 --> 00:07:18,000
which is imported from React,

138
00:07:18,000 --> 00:07:20,000
so which has nothing to do with NextJS,

139
00:07:20,000 --> 00:07:23,000
but which instead works the way you know it from React,

140
00:07:24,000 --> 00:07:29,000
which gives you a ref, like a imageInput ref

141
00:07:30,000 --> 00:07:34,000
that you can then connect to an HTML element.

142
00:07:34,000 --> 00:07:37,000
And I'll actually just name it imageInput.

143
00:07:38,000 --> 00:07:40,000
But now we can use that ref

144
00:07:40,000 --> 00:07:42,000
and set it as a value for the ref prop

145
00:07:42,000 --> 00:07:47,000
on this imageInput field here like this.

146
00:07:47,000 --> 00:07:51,000
And that now allows us to use this ref

147
00:07:51,000 --> 00:07:54,000
to trigger the click method.

148
00:07:54,000 --> 00:07:58,000
However, of course, we have to access .current first

149
00:07:58,000 --> 00:08:00,000
because that then gives us access

150
00:08:00,000 --> 00:08:03,000
to the actual connected element and object.

151
00:08:04,000 --> 00:08:06,000
But that then is the code we can use

152
00:08:06,000 --> 00:08:09,000
to trigger a click on this input,

153
00:08:09,000 --> 00:08:11,000
which is connected to this ref.

154
00:08:13,000 --> 00:08:15,000
With that, if I now click my own button,

155
00:08:15,000 --> 00:08:18,000
this image picker opens up again.

156
00:08:19,000 --> 00:08:22,000
But now as a final step, I wanna make sure that as soon

157
00:08:22,000 --> 00:08:26,000
as I pick an image, I also show a preview of that here.

