using useEffect effectively
I started exploring react's functional component and really like how it has made the code neat and compressed the component to a few lines. We are going to discuss 3 most used scenarios there must be more where you can make use of useEffect
hook. In this article, I'll be comparing useEffect
with classical component hooks and explaining the whys and hows. So yeah here's the list of use cases.
- component with an api call
- component which receives props and you want to listen to the change of props
- component which receives props and you want to make a change in the props before the first render.
Before jumping into examples and code lets dissect what useEffect
is and how it works. It takes two arguments first is a callback and second is an array for listening.
Component with an API call
So in the classical components, we used to make our api calls or any work we want to do before or after rendering of the component we mostly use hooks like componentWillMount
or componentDidMount
something like this
class UserList extends Component {
state = {
users: undefined,
loading: true,
}
componentDidMount() {
fetch("https://api.github.com/users")
.then(res => res.json())
.then(users => {
console.log(users)
this.setState({ users, loading: false })
}
}
render() {
const { loading, users } = this.props;
if (loading) return <Loader size="small" />
return (
<div className="container">
{
users.map((user, index) => <UserCard key={index} data={user} />)
}
</div>
)
}
}
Now this code in functional component looks something like this
function UserList() {
const [loading, setLoading] = useState(true)
const [users, setUsers] = useState(undefined)
useEffect(() => {
fetch("https://api.github.com/users")
.then(res => res.json())
.then(users => {
setLoading(false)
setUsers(users)
}
},[])
if (loading) return <Loader size="small" />
return (
<div className="container">
{
users.map((user, index) => <UserCard key={index} data={user} />)
}
</div>
)
}
Notice in this component we have an empty array defined as a second argument to useEffect
which means it will runs only one time.
If you pass an empty array ([]), the props and state inside the effect will always have their initial values. While passing [] as the second argument is closer to the familiar componentDidMount and componentWillUnmount mental model
Component receives props
So in the classical components, if we want to listen to a props change we mostly listen in componentWillReceiveProps
hook and then change the structure of data or if we want to just set it in a state we do that all in there something like this.
class UserList extends Component {
state = {
users: undefined,
loading: true,
}
componentWillReceiveProps({ users }) {
this.setState({ users, loading: false })
}
render() {
const { loading, users } = this.props;
if (loading) return <Loader size="small" />
return (
<div className="container">
{
users.map((user, index) => <UserCard key={index} data={user} />)
}
</div>
)
}
}
Now this code in a functional component will look something like this
function UserList({ apiUsers }) {
const [loading, setLoading] = useState(true)
const [users, setUsers] = useState(undefined)
useEffect(() => {
setUsers(apiUsers)
setLoading(false)
}, [apiUsers])
if (loading) return <Loader size="small" />
return (
<div className="container">
{
users.map((user, index) => <UserCard key={index} data={user} />)
}
</div>
)
}
Here we are listening to the change in apiUsers
prop so every time there's a change in it useEffect
with its callback gets called
Component receives props(first render)
So we sometimes have to deal with a component where we don't want it to listen to any prop but just render the component with whatever props in first go. It looks something like this.
class UserList extends Component {
state = {
users: undefined,
loading: true,
}
componentDidMount() {
const {users} = this.props;
this.setState({users, loading: false})
}
render() {
const { loading, users } = this.props;
if (loading) return <Loader size="small" />
return (
<div className="container">
{
users.map((user, index) => <UserCard key={index} data={user} />)
}
</div>
)
}
}
Now this code in a functional component will look something like this
function UserList({ apiUsers }) {
const [loading, setLoading] = useState(true)
const [users, setUsers] = useState(undefined)
useEffect(() => {
setUsers(apiUsers)
setLoading(false)
}, [])
if (loading) return <Loader size="small" />
return (
<div className="container">
{
users.map((user, index) => <UserCard key={index} data={user} />)
}
</div>
)
}
Notice in this component we have an empty array defined as a second arguement to useEffect
which means it will work as componentDidMount
and runs only one time after.
Fin
So that was it! I'd love to hear your thoughts on this. Please do comment or email me about your takeout on this and if I miss something do let me know.🤝