What you'll learn:
The multi-cursor feature in the editor makes it easy for us to update our code in multiple places at once. By definition, it makes it easy for us to repeat ourselves.
However, in general, we want to write code once and reuse it in the future. We donβt want to repeat ourselves. This is the DRY principle in coding.
DRY: Do not Repeat Yourself
Iβll ironically repeat, do not repeat yourself!
Looking at our Frames,
<Frame>
<Frame position="relative">π Apple</Frame>
<Frame position="relative"> π₯ Avocado</Frame>
<Frame position="relative">πΆ Pepper</Frame>
</Frame>
Each one is supposed to be an individual radio button that differs only in whatβs inside the <Frame>
tags. The <Frame>
tag itself and its attributes are all the same.
Radio
componentWhen we notice this kind of repetition, we should extract our code into a separate component.
Let's replace the radio button Frames with <Radio>
<Frame>
<Radio>π Apple</Radio>
<Radio> π₯ Avocado</Radio>
<Radio>πΆ Pepper</Radio>
</Frame>
Again, were are using the <Radio>
tag assuming it already exists. It's important to play with different options before implementing them.
For example, we could make our radio label an attribute or a child of the <Radio>
tag.
However, we'll go ahead and implement Radio
.
function Radio() {
return <Frame position="relative"></Frame>
}
But what do we put inside the <Frame>
tag?
props.children
Here we put the label of the radio as the child of the <Radio>
tag. Therefore, unlike our previous modules, our value is not an attribute of the tag.
<Frame>
<Radio>π Apple</Radio>
<Radio> π₯ Avocado</Radio>
<Radio>πΆ Pepper</Radio>
</Frame>
However, we can access the children of a tag in a very similar way to accessing the attributes.
function Radio(props){
return(
<Frame position="relative">{props.children}</Frame>
)
}
All the children of the tag are available as props.children
, but donβt forget to add the pair of curly brackets!
Our preview should look like what we had in the beginning:
Now, letβs add the circle in front of the label. To save time, we'll paste in some code in the Radio
component return
.
function Radio(props){
return(
<Frame
position="relative"
size="auto"
style={{ display: "flex", marginBottom: 10 }}
background="null"
>
<Frame
size={30}
shadow="0 1px 5px 0 rgba(0,0,0,0.3)"
background="white"
borderRadius={15}
position="relative"
style={{ marginRight: 10 }}
/>
{props.children}
</Frame>
)
}
We still have our original Radio
<Frame>
, but there is extra styling and a new <Frame>
that resembles a white circle.
selected
attributeNext, we want to show a smaller purple circle inside the white circle to indicate if this radio is selected.
To signal if a <Radio>
is selected, we can add a selected
attribute.
<Frame>
<Radio selected>π Apple</Radio>
<Radio> π₯ Avocado</Radio>
<Radio>πΆ Pepper</Radio>
</Frame>
However, the Radio
component has to understand what this selected
means and what to do.
Let's add the small purple circle inside the outer white circle. Note that we have to split up the white circle <Frame />
into <Frame></Frame>
.
function Radio(props){
return(
<Frame
position="relative"
size="auto"
style={{ display: "flex", marginBottom: 10 }}
background="null"
>
<Frame
size={30}
shadow="0 1px 5px 0 rgba(0,0,0,0.3)"
background="white"
borderRadius={15}
position="relative"
style={{ marginRight: 10 }}
>
<Frame
size={20}
borderRadius={10}
background="purple"
center
/>
</Frame>
{props.children}
</Frame>
)
}
We can now add code into our purple circle to determine whether to show or hide itself according to which radio was selected
. We'll use the animate
attribute.
<Frame
...
>
<Frame
size={20}
borderRadius={10}
background="purple"
center
animate={{scale:1}}
/>
</Frame>
If the scale
is equal to 1
, we will see the purple circle. Otherwise, if scale is equal to 0
it will be hidden. We can use a conditional to set scale
equal to 1
or 0
.
<Frame
...
>
<Frame
size={20}
borderRadius={10}
background="purple"
center
animate={{scale: props.selected ? 1 : 0}}
/>
</Frame>
Do you remember what this is? It's a ternary operator! Essentially, a ternary operator is a condensed form of if then else statement.
Our preview should look like this:
RadioGroup
Taking another look at our RadioGroup
component, we have three hardcoded options and there are only three vegetables! Itβs not very useful. We want to make it reusable so that we could, for example, list different options.
Letβs say we have the options available as an array in our App
component.
function App() {
let options = [
"π Apple",
"π₯ Avocado",
"πΆ Pepper",
"π₯ Carrot",
"π Burger",
"π₯ Kiwi fruit",
"π Banana"
]
return (
<div className="App">
<RadioGroup />
</div>
)
}
We want to be able to pass it to RadioGroup component with something like this:
function App() {
let options = [
'π Apple',
'π₯ Avocado',
'πΆ Pepper',
'π₯ Carrot',
'π Burger',
'π₯ Kiwi fruit',
'π Banana',
]
return (
<div className="App">
<RadioGroup choices={options} />
</div>
)
}
map
functionHow do we use options
to populate our RadioGroup
? You tell me! We've done it before using map
!
First, we'll add props
to RadioGroup
.
function RadioGroup(props) {
...
}
Second, we'll replace the hardcoded Frames with the map
function. Don't forget the curly brackets!
function RadioGroup(props) {
return (
<Frame>
{
props.choices.map(choice => <Radio>{choice}</Radio>)
}
</Frame>
)
}
Remember, this is an arrow function!
key
attributeFinally, don't forget to add the key
prop to avoid console warnings.
function RadioGroup(props) {
return (
<Frame>
{props.choices.map(choice => (
<Radio key={choice}>{choice}</Radio>
))}
</Frame>
)
}
Our Radio Group should now look like this:
We now have a reusable RadioGroup
. We can pass whatever options we want as the choices prop, and theyβll be rendered properly as radio buttons.
In the next post, we will style our Radio Group by passing along props
.