Originally published at Codegram's blog
This is the second part of the React workshop series created by our team. Let's learn about how to create stateful components using React hooks.
It's been a while but finally awe have released the second part of our React workshop. Remember that you can download the code from the first part in here. If you want to follow along just checkout the part-1
tag and update the code as you read.
Introduction
In the first part we created our first components using React, but we ended the workshop in a bit of a cliffhanger and encountered a bug 🐛. Today we are going to continue our application and start writing some stateful components.
Stateful components with React hooks
We have created a bunch of stateless components or usually also called "dumb components". Starting now we are going to learn how to add some state to them. First we need to learn what is a React hook
!
useWhat? First overview of hooks
A hook is just a function, period. We can write our hooks and the convention is that they start with the prefix use
. The hooks can only be executed inside components (or other hooks) so usually the hook just return something that can be used by the component.
A common use of hooks is to reuse some business logic. First let's create a Cat
😸 component (you need to create the corresponding folder structure as we did before):
// src/cats/components/cat/Cat.js
import React from 'react'
import PropTypes from 'prop-types'
const Cat = ({ name, score }) => {
const onClick = () => {
score += 1
console.log(`This is your new score: ${score}`)
}
return (
<div>
<p>{name}</p>
<p>Score: {score}/10</p>
<button onClick={onClick}>Increase score</button>
</div>
)
}
Cat.propTypes = {
name: PropTypes.string.isRequired,
score: PropTypes.number.isRequired,
}
export default Cat
Then, use the Cat
component in our application to add a new cat to the view (😸 are also awesome so give them a good score!):
// src/App.js
import React from 'react'
import Dog from './dogs/components/dog/Dog'
import Cat from './cats/components/cat/Cat'
const App = () => {
return (
<>
<Dog name="Boira" score={13} />
<Dog name="Black" score={13} />
<Cat name="Uhura" score={13} />
</>
)
}
export default App
Nothing really fancy here! Now we have two components that are mostly identical. Let's fix this!. Let's create a pets
module with a hooks folder, and we are going to create our first hook. We are going to copy the onClick
function from the Dog
component and use it inside our hook.
// src/pets/hooks/useScore.js
const useScore = (score) => {
return {
increaseScore: () => {
score += 1
console.log(`This is your new score: ${score}`)
},
}
}
export default useScore
Notice that the hook is just a function that receives some value, score
in this case, and returns a new object. I added the increaseScore
function to the object so now we can use it inside our components.
Let's use it in the Dog
component:
// src/dogs/components/dog/Dog.js
import React from 'react'
import PropTypes from 'prop-types'
import useScore from '../../../pets/hooks/useScore'
const Dog = ({ name, score }) => {
const { increaseScore } = useScore(score)
return (
<div>
<p>{name}</p>
<p>Score: {score}/10</p>
<button onClick={increaseScore}>Increase score</button>
</div>
)
}
Dog.propTypes = {
name: PropTypes.string.isRequired,
score: PropTypes.number.isRequired,
}
export default Dog
Do the same for the Cat component, and we are done here! We have moved some common logic to a hook so now we can focus on fixing our problem. We need to make our view aware of that change in the score or, in other words, make the component stateful!
Stateful components using useState
React already includes some hooks. We are going to use useState
to add some state
to our components. The difference between a prop and the state is that we can change the state to re-render our component when something changes!
Remember that we can use hooks inside other hooks so let's use the useState
hook inside our custom hook useScore
like this:
// src/pets/hooks/useScore.js
import { useState } from 'react'
const useScore = (baseScore) => {
const [score, setScore] = useState(baseScore)
return {
score,
increaseScore: () => {
setScore(score + 1)
console.log(`This is your new score: ${score}`)
},
}
}
export default useScore
Hold up! There is a lot to digest in the previous snippet so let's review it step by step. First, we rename the hook argument to baseScore
and passed it to the useState
method, this is the initial value of our state. The useState
function returns an array where the first element is our state and the second element is a function to replace our state. I used the word "replace" intentionally because we need to provide a new object always, otherwise it will not be updated (embrace the immutability
!).
Then I added the score
to the object that our custom hook is returning, so we have access to it in our component. Finally, I used the setScore
function to set a new state when the increaseScore
function is called. Neat, right?
Let's see how to use it in our components. I am gonna change the Dog
component, and I am gonna leave the Cat
component for yourselves as an excercise:
// src/dogs/components/dog/Dog.js
import React from "react";
import PropTypes from "prop-types";
import useScore from "../../../pets/hooks/useScore";
const Dog = ({ name, initialScore }) => {
const { score, increaseScore } = useScore(initialScore);
return (
<div>
<p>{name}</p>
<p>Score: {score}/10</p>
<button onClick={increaseScore}>Increase score</button>
</div>
);
};
Dog.propTypes = {
name: PropTypes.string.isRequired,
**initialScore: PropTypes.number.isRequired,**
};
export default Dog;
I also changed the Dog
's score
prop to initialScore
so we need to update our application as well:
// src/App.js
import React from 'react'
import Dog from './dogs/components/dog/Dog'
import Cat from './cats/components/cat/Cat'
const App = () => {
return (
<>
<Dog name="Boira" initialScore={13} />
<Dog name="Black" initialScore={13} />
<Cat name="Uhura" initialScore={13} />
</>
)
}
export default App
After making all the changes you can refresh your browser and test your application. The view is also updated when we press the button! 🎉
React includes a small list of hooks: https://reactjs.org/docs/hooks-reference.html but creating your own is straightforward.
Conclusion
In this article we learned about how to add state to our a React application and written our first React hooks. Our application is basic but for a bigger application is better to rely on a state management library and that's the topic for our next article!.
You can find the code examples in this repository. I also tagged the progress for part 2 in case you want to check the repository at this specific moment.
Cover photo by Henar Langa