using useEffect effectively
Tags: react, hooks
Published: 2020-03-08 14:27:46

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.
1. component with an api call 
2. component which receives props and you want to listen to the change of props 
3. 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

```javascript
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

```javascript
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.

```javascript
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

```javascript
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.

```javascript
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

```javascript
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.🤝