React Hooks in a Nutshell (Incl Data Fetching, Custom Hooks, Context and Usages)
BitPunchZ
Posted on October 9, 2019
So this is my attempt to give people who are new to hooks a quick overview of the most important hooks that you should know alongside basic examples of usage of each one to get you started.
Now off we go .
General intro
There are multiple (seemingly unrelated) problems that hooks attempt to tackle, but for the sake of keeping this as short as possible, you should know that hooks allows you to:
- Have state in your functional component
- Reuse a piece of state logic in multiple components
- Simplify your logic when it gets too big
Let's start with the first hook
useState
useState is the hooks way to have state in your functional component, so for example say you're implementing a clap counter, you'd normally achieve this by implementing a class based components like this:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
claps: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.claps} times</p>
<button onClick={() => this.setState({ claps: this.state.claps + 1 })}>
Clap
</button>
</div>
);
}
}
well, in hooks you can do the same writing this:
function Example() {
const [claps, setClaps] = useState(0);
return (
<div>
<p>You clapped {claps} times</p>
<button onClick={() => setClaps(claps + 1)}>
Clap
</button>
</div>
);
}
And voila, you got a fully functional state (pun intended) .
That const [claps, setClaps] = useState(0);
line utilizes array destructuring, which was introduced in ES6
claps
is our state field, and it will have an initial value of whatever we pass to useState
, which is in that case was 0
, so initially claps will be equal to 0
setClaps
is the function that we will use to modify that claps
state field, as you can see happen when we click on our clap button, this will fire the onClick
and the setClaps
will be called with the existing value of claps
plus 1
, which will be the claps
field new value
And that covers the first hook !
useEffect
The useEffect
hook can be used to emulate alot of the existing lifecycle methods like componentDidMount
, componentWillUnmount
and componentDidUpdate
(and some of the older lifecycle methods ofocourse like componentWillRecieveProps
)
But before we see usage example, you should know that useEffect
take in 2 params, a function and an array.
The array (let's call it dependencies array) will consist of the names of the values that, when changed, will have the function - the first param - called
what if we didn't have a dependency array?
- that means that the useEffect function -it's first param- will run on every render, which is usually not what we want
In a later example we'll mention what happen if the dependency array is empty
React to state/props change
So let's see an example
Say you want to do something every time the user claps, for the sake of an example, say you want to console.log
a "You clapped" message, with the useEffect
hook we can do that this way
function Example() {
const [claps, setClaps] = useState(0);
useEffect(()=>{
console.log("You clapped");
},[claps])
return (
<div>
<p>You clapped {claps} times</p>
<button onClick={() => setClaps(claps + 1)}>
Clap
</button>
</div>
);
}
So what happens here is that every time a state field changes, react checks all of our useEffect (yes there can be a multiple of them in our code, just like there can be multiple state fields defined using useState
) and it fires all the functions in the useEffect
that, in their dependency array, have the field that got changed
So in our case, when we click on our clap button, the setClaps
function is called, the claps
state field gets changed, which leads to this useEffect first param (it's function) to be called since it's dependency array contains the claps
field
...
useEffect(()=>{
console.log("You clapped");
},[claps])
...
So this was basically how to emulate componentDidUpdate
This structure can of course also be used to call your useEffect hook function when a prop change, simply by adding whatever props you want to be taken into consideration to the dependency array, so if claps are gotten from the component's parent for example:
function({claps}){
useEffect(()=>{
console.log("You clapped");
},[claps])
...
}
Also, since it's called a dependency array, remember that you can have multiple values in it, and the function will get fired if one or more of the values in the dependency array get changed
Data fetching
useEffect can also be used to fetch data, but the key question to ask before using this hook to fetch data is :
what if the dependency array was empty?
- that means the hooks won't run on every render since we explicitly told it to not watch over any variables, so it will only run on mounting
Which is usually what we want when we want to fetch data
So now that we know how to make the useEffect only run on mounting (emulating the componentDidMount lifecycle), fetching data is as simply as doing this:
function App(){
const [data,setData] = useState([]);
useEffect(()=>{
const data = await axios('https://datafetchingexample/data');
setData(data);
},[])
...
}
Cleaning up
Next up is figuring out how to use useEffect
to do any clean up that we want to do in our component
function App(){
const [data,setData] = useState([]);
useEffect(()=>{
const source = axios.CancelToken.source();
const data = await axios('https://datafetchingexample/data');
setData(data);
return () => {
source.cancel();
};
},[])
...
}
So as you might've noticed, we added a return in our hook, this return function will run when the component unmounts, making it the perfect place to do any cleaning up (closing a socket, unsubscribing, cancelling a request, etc...basically same usage as componentWillUnMount)
useContext
Next up, using context with hooks
Context is, as you may know, react's way to manage state across components, basically it's react's own redux in a way
It's used when you have some data in a component that you want descendants of that components (direct children or indirect descendants in general) to have access to, so for example say we have a component that fetches data and you want to pass that data to your child, obvious way to do that is using props, but if you want to have that data in your grand grand grand grand child...that's where using props can turn into more of a hassle and where using context makes more sense.
For the sake of explanation however, let's say you want to pass this data to your direct child
So first, we'll create a context that has a value of an empty object
const DataContext = React.createContext({});
Next you should wrap the component you want to pass the context to in
<DataContext value=somevalue></DataContext>
Which is what we did to our child component, all we have to do this is just determine the value of the context through the value property
(in that case we want to pass the data field), so we did
...
const DataContext = React.createContext({});
function App(){
const [data,setData] = useState([]);
useEffect(()=>{
const source = axios.CancelToken.source();
const data = await axios('https://datafetchingexample/data');
setData(data);
return () => {
source.cancel();
};
},[])
return (
<DataContext value={{data}}>
<Child/>
</DataContext>
)
}
Now moving on to our child component, all we have to do is use the useContext
hook, pass the context object that we want to it, and simply get data that we added in the value
attribute
...
function Child(){
const {data} = useContext(DataContext);
return (
<ul>
data.map(v=>{
return (
<li>
{v.value}
</li>
)
})
</ul>
)
}
So now that we've covered arguably the 3 most popular hooks, let's talk about the general rules of hooks
Hooks Rules
Only call hooks at the top level
This means that you should not use hooks inside loops,if conditions or nested functions, you should always use hooks at the top level of your react function, that's because hooks depend on the order that they are initialized in, so if you for example add a hooks inside a hook in an if condition, that if condition might not be happening in the next render, leading to a disorder in the hooks, we'll talk more about this in another article
Don't call hooks in javascript functions
You can call hooks from 2 places
- react functional components
- custom hooks, which we will talk about next
Custom hooks
Now for the last and the core piece of react hooks, making your own custom hooks.
Custom hooks are javascript functions whose name start with use
and it has the ability to call other hooks (custom or built in)
Building a custom hook means that you get to extract a piece of logic for you to use it in multiple places, for example, say you got a couple of inputs that accepts name and age
function InputForm(){
const [name,setName] = useState("")
const [age,setAge] = useState(0)
return (
<div>
<input type="text" placeholder="Enter your name" value={name} onChange={(e)=>setName(e.target.value)/>
<input type="number" placeholder="Enter your age" value={age} onChange={(e)=>setAge(e.target.value)}/>
</div>
)
}
Now, basically all inputs in our app will have a similar structure, input field with value,onChange attributes, and not only in this file, using state to handle input can in multiple files that you have, custom hooks will let us extract that piece of reusable logic to use it elsewhere
it will look something like this :
function useFormInput(initialValue){
const [value,setValue] = useState(initialValue);
function onChange(e){
setValue(e.target.value);
}
return {value,onChange};
}
function InputForm(){
const name = useFormInput("")
const age = useFormInput(0)
return (
<div>
<input type="text" placeholder="Enter your name" {...name} />
<input type="number" placeholder="Enter your age" {...age} />
</div>
)
}
Cleaner isn't it? this will have the same affect as the regular way of doing things but now you have a reusable hook that let you have inputs with their changing functionality anywhere in the app, just use the hooks, destructure the value returned in your input tags, and you're good to go !
If you think you need to practice more on the subject while building 4 projects, I encourage you to take a look at this course :
Posted on October 9, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 9, 2019