I was going through the react course on @FrontendMasters by @holtbt and I noticed how he used a custom hook to return a closure of react component, it's state object and the state modifier to create a reusable component.
When I want to create a component for rendering similar elements with different data I just go ahead a create a component which excepts the dynamic data as props.
Let's take an example where we want to create multiple dropdown
elements with different options. I would go ahead and create a component something like this
// DropdownElement.js
import React, { useState } from 'react';
const DropdownElement = props => {
const { dropdownLabel, options } = props;
const [selectedValue, updateSelectedValue] = useState(options[0]);
return (
<>
<label htmlFor={dropdownLabel}>
{dropdownLabel}
</label>
<select
id={dropdownLabel}
value={selectedValue}
onChange={e => updateSelectedValue(e.target.value)}
onBlur={e => updateSelectedValue(e.target.value)}
disabled={!options.length}
>
{options.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</>
);
};
export default DropdownElement;
Now if we want to create any dropdown element, we can use <DropdownElement />
passing in props with it.
Let's say we want to create a filter component for shoes which has Color
and Size
dropdown fields.
//shoeFilter.js
import React from 'react';
import DropdownElement from './DropdownElement';
const ShoeFilter = () => (
<>
<DropdownElement
dropdownLabel="Shoe Color"
options={["black", "red", "white"]}
/>
<DropdownElement
dropdownLabel="Shoe Size"
options={["6", "7", "8", "9", "10"]}
/>
</>
);
export default ShoeFilter;
This will create two dropdowns Shoe Color
and Shoe Size
. This is what people generally do, or at least this is what I do. There is nothing wrong with it. But, what if we could give the composite component <DropdownElement />
any name we want when we use it? Say for Shoe Color
we could call it with <ShoeColorDropdown />
and for Shoe Size
it could be <ShoeSizeDropdown />
This would certainly increase readability. So how can we do this?
import React, { useState } from 'react';
const useDropdown = (dropdownLabel, options) => {
const [selectedValue, updateSelectedValue] = useState(options[0]);
const DropdownComponent () => (
<>
<label htmlFor={dropdownLabel}>
{dropdownLabel}
</label>
<select
id={dropdownLabel}
value={selectedValue}
onChange={e => updateSelectedValue(e.target.value)}
onBlur={e => updateSelectedValue(e.target.value)}
disabled={!options.length}
>
{options.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</>
);
return DropdownComponent;
};
const ShoeFilter = () => {
const ShoeColorDropdown = useDropdown("Shoe Color", ["black", "red", "white"]);
const ShoeSizeDropdown = useDropdown("Shoe Size", ["6", "7", "8", "9", "10"]);
return (
<>
<ShoeColorDropdown />
<ShoeSizeDropdown />
</>
);
};
So what is happening here is I am giving a name to the closure which is returned when useDropdown()
is invoked and since that closure returns a react component we can go ahead and render it.
Now I have used this component just to render some data, we can extend this by returning an array of objects related to the component like its state and state modifier so the parent component can directly access the component data and maybe also change it. This is a custom hook and the next post will explain it well. I will update on twitter once it's out, please show me some love there :P
Let me know your thought's.
If anyone is getting started with react, this course on @FrontendMasters by @holtbt is a great starting point. I got the idea for this pattern from his lecture. Also, the subscription to Frontend Masters is really really worth it. The quality of content is just amazing. Kudos to @1Marc for putting together an amazing platform.
Top comments (2)
The problem with the hook approach is that you actually don't solve any problem. Performance-wise
DropdownElement
written as component is parsed by the engine once, while inside the hook function forDropdownComponent
is allocated on every hook call. While ofc the effect is very little, there is no benefit that can out-weight the loss.Exactly! Writing custom hooks just for readability while compromising with the performance is not a good approach.
I would've loved if there was some actual problem-solving in this post.