Functional components create a React component in a simple and clean way but lack some functionalities such as states and life cycle, which class components can provide. “Hooks” is another way to write reusable code and lets you use state and other React features without writing a class.
Hooks System
With Hook functions, you can use states, lifecycle-like methods (effects), and refs in a functional component.
Primitive Hooks
- useState()
- useEffect()
- useRef()
- useContext()
- useCallback()
- useMemo()
- useReducer()
- useImperativeHandle()
- useLayoutEffect()
- useDebugValue()
You can make custom hooks by combining primitive hooks.
Using the State Hook
The “useState(inital_value)” function returns a pair:
- the current state value
- a function to update a state
import React, {useState} from 'react';
const Test = (props) => {
// a new state variable and a state function
const [clickCount, setClickCount] = useState(0);
return (
<div>
<button onClick={() =>
setClickCount(clickCount + 1)}>
Click
</button>
<h3>{clickCount}</h3>
</div>
);
}
export default Test;
Using the Effect Hook
Effect hooks perform additional operations – side effects -. They serve the same purpose as the lifecycle methods of a class component.
The Effect Hook can be used in one of three scenarios depending on the second argument:
- an empty array []
- When a component is rendered (1) for the first time
- no argument (default)
- When a component is rendered (1) for the first time and (2) whenever it re-renders
- an array with data [data]
- When a component is rendered (1) for the first time and (2) whenever it re-renders, and some piece of data has changed since the last render
useEffect(() => {
console.log('useEffect');
}, []);
useEffect(() => {
console.log('useEffect');
});
useEffect(() => {
console.log('useEffect');
}, [data]);
The following example does the same effect as componentDidMount. It mimics the data loading with the timer.
import React, {useState, useEffect} from 'react';
const Test = (props) => {
const [items, setItems] = useState(null);
useEffect(() => {
setTimeout(() => {
setItems([1,2,3]);
}, 3000);
}, []); // first-time only
if (items == null) {
return (
<div>Loading...</div>
);
}
return (
<div>
<h3>You have {items.length} items.</h3>
</div>
);
}
export default Test;
Using Effect Hooks with Cleanup
An effect hook runs for every render. You might want a mechanism to do some cleanup actions from the previous render before running the effect next time. An effect hook may return a function that cleans up after each render.
useEffect(() => {
console.log('useEffect');
// clean up function
return () => {
console.log('cleanup');
};
});
Using Ref Hooks
The “useRef(inital_value)” function returns a reference object that is used to access the DOM element. If you pass a ref object to a React <div ref={myRef} />, React sets its “current” property to the corresponding DOM node.
import React, {useRef} from 'react';
const Test = (props) => {
const myRef = useRef('');
return (
<div>
<input ref={myRef} type="text" />
<button onClick={ () => {
alert(myRef.current.value);
}}>Click</button>
</div>
);
}
export default Test;
Custom Hooks
- A custom hook is created by extracting hook-related code out of a “function component” and is the best way to create reusable code.
- Each custom hook should perform only one task, such as data fetching.
- A custom hook always makes use of at least one primitive hook (useState, useEffect) internally.
import {useState, useEffect} from 'react';
const useVideos = (seartchTerm) => {
const [videos, setVideos] = useState([]);
useEffect(() => {
searchVideos(seartchTerm);
}, [seartchTerm]);
const searchVideos = async (term) => {
const response = await youtube.get(
'https://www.googleapis.com/youtube/v3/search', {
params: {
q: term,
part: 'snippet',
type: 'video',
maxResults: 5,
key: '---------------'
}
});
setVideos(response.data.items);
};
return [videos, searchVideos];
};
export default useVideos;
Custom Hooks can be used like this.
import React from 'react';
import useVideos from '../../hooks/useVideos';
const MyTubeApp = (props) => {
const [videos, searchVideos] = useVideos('movie');
const videoList = videos.map(video => {
return (
<div>
{video.snippet.title}
</div>
);
});
return (
<div>
{videoList}
</div>
);
};
export default MyTubeApp;