What is a Custom Hook?
- Tram Ho
Preamble
You must have used the useState, useEffect, useRef … hooks, but have you heard of custom hooks? Or maybe like me, heard, read, but also do not understand why to have it, how to use it? So after a while I did my own research, I refined some of the following knowledge, hoping to be of some help
Body post
I would like to introduce to everyone Teo – my friend is a main fanboy of class component and never wrote in a functional component + hook style. One day a handsome guest came to see Teo and offered an app with a simple UI like this
Me: I designed the same UI for you and made about 1000 pictures of all shapes here a show example 2 only, with each image passing every second will randomly change 1 color from # 00000 to # 999999 for the background color.
Teo: OK HKT, (thinking: copy the code is tired, 1000 pictures)
Being a main fanboy of the class component, Teo has code as follows: App.js
1 2 3 4 5 6 7 8 9 10 11 | function App() { return ( <div> <BigCircle /> <BigRectangle /> </div> ); } export default App |
components/BigCircle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class BigCircle extends React.Component { constructor(props){ super(props) this.state = { color: 'green' } } componentDidMount() { this.intervalColor = setInterval(() => { const newColor = Math.floor(Math.random() * 999999) this.setState({ color: `#${newColor}` }) }, 1000) } componentWillUnmount(){ clearInterval(this.intervalColor) } render() { const { color } = this.state return ( <div className="big-circle" style={{backgroundColor: color}}> Big Circle </div> ); } } export default BigCircle; |
I happened to be passing by and saw that Teo was copying intoxicatedly, then adjusted the css back to a new picture, so I asked Teo what was doing?
Teo: His client asked me to make 1000 different pictures but the logic is the same so I have to copy it 1000 times and then adjust the css again. Is there a way to make it faster?
Me: Before talking about fast or not, I will talk about the good or not, now in your opinion is this code good?
Teo: I don’t think there is any problem normally, running ok, right?
Me: With the same logic, I have to copy and paste all of these places, what’s good? What if your client wants to change it to 3s, then change the color once? Thousands of files that fix must be broken
Teo: Yeah, weird is to write it somewhere to share it, huh? It is okay to separate the setInterval function into the general function.
Teo corrected
1 2 3 4 5 6 7 | function setIntervalColor(interval, cb) { return setInterval(() => { const newColor = Math.floor(Math.random() * 999999) cb(`#${newColor}`) }, interval); } |
components/BigCircle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class BigCircle extends React.Component { constructor(props){ super(props) this.state = { color: 'green' } } componentDidMount(){ this.intervalColor = setIntervalColor(1000, (newColor) => { this.setState({color: newColor}) }) } componentWillUnmount(){ clearInterval(this.intervalColor) } render() { const { color } = this.state return ( <div className="big-circle" style={{backgroundColor: color}}> Big Circle </div> ); } } export default BigCircle; |
Me: So now he asks you, before you delete it from the DOM and set it back to red, how are you going to fix all 1000 components?
Teo: Sax, it’s difficult, then what should we do?
Me: Currently, the logic is duplicated, there is a state color in every component to get the current color, then in componentDidMount declare an interval to setState to change color, then componentWillUnmount clear that interval.
Teo: But there is no other way you can.
Me: Rewrite with a functional component with hook
components/BigCircle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function BigCircle(props) { const [color, setColor] = useState('green') useEffect(() => { const intervalColor = setInterval(() => { const newColor = Math.floor(Math.random() * 999999) setColor(`#${newColor}`) }, 1000); return () => { clearInterval(intervalColor) } }, []) return ( <div className="big-circle" style={{backgroundColor: color}}> Big Circle </div> ); } export default BigCircle; |
Me: OK, have you ever heard of custom hooks?
Teo: No, sounds strange?
Me: In this case, what the component wants to receive is just the color code, and the rest of the code just serves the purpose of returning that color code, right? I copy this logic for each component, it’s not good, why don’t I separate it to avoid duplication and just return it to the component that it wants to receive as color code.
Teo: Why is it separable, it is the state associated with the component, and then the mount / unmount handle of that component.
Me: Yes, back then it didn’t work, but since the concept of customHook came out, it’s been easy. Basically custom hooks are functions that begin with ‘use’ (not starting with ‘use’ which is an error) that can use other hooks but instead of returning to JSX it returns something value, so that I write back for you
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function useMagicColor(props) { const [color, setColor] = useState('green') useEffect(() => { const intervalColor = setInterval(() => { const newColor = Math.floor(Math.random() * 999999) setColor(`#${newColor}`) }, 1000); return () => { clearInterval(intervalColor) } }, []) return color } export {useMagicColor}; |
Me: See, it can also use useState, useEffect or other hooks without returning JSX, now at which component you need to use this logic, you import and then call useMagicColor like calling a normal function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import React from 'react'; import './styles.scss' import { useMagicColor } from '../customHooks' function BigCircle(props) { const color = useMagicColor() return ( <div className="big-circle" style={{backgroundColor: color}}> Big Circle </div> ); } export default BigCircle; |
End
This custom hook is really very useful, it helps seperate logic code out of the component to make the code easier to maintain and more beautiful. On this topic, but there will be an example of using a very practical custom hook that for sure everyone will have to fetch data using a custom hook. Thanks for reading my post