useEffect Debug

In this practice, we're debugging an app that uses the useEffect hook. The app has 8 errors (including console warnings) that need to be fixed for it to function properly.

Understanding the Problem

The app should work as follows:

Our task is to identify and fix 8 errors in the application.

Devising a Plan

  1. Examine each component's code for possible issues
  2. Check all useEffect hooks for correct implementation
  3. Look for missing dependencies in dependency arrays
  4. Verify localStorage is being used correctly
  5. Check for async function handling in useEffect
  6. Look for missing cleanup functions
  7. Check for React Router usage issues
  8. Verify component naming consistency

Carrying Out the Plan

Issue 1: Missing useNavigate in Unmounted.jsx

The navigate function is used but not defined in the Unmounted component.

Original code:

import { useNavigate } from 'react-router-dom';

const Unmounted = () => {
  return (
    <div className='unmounted'>
      <h1>Welcome</h1>
      <button onClick={() => navigate('/mount')}>Login</button>
    </div>
  );
};

Fixed code:

import { useNavigate } from 'react-router-dom';

const Unmounted = () => {
  const navigate = useNavigate(); // Added this line
  
  return (
    <div className='unmounted'>
      <h1>Welcome</h1>
      <button onClick={() => navigate('/mount')}>Login</button>
    </div>
  );
};

Issue 2: Component Name Mismatch in RandomUser.jsx

The component name doesn't match the filename and export.

Original code:

const RandomUserTwo = () => {
  // Component code
};

export default RandomUserTwo;

Fixed code:

const RandomUser = () => { // Changed RandomUserTwo to RandomUser
  // Component code
};

export default RandomUser;

Issue 3: Async Function in useEffect without Proper Handling

The first useEffect in RandomUser.jsx has an async function but doesn't properly handle it.

Original code:

useEffect(() => {
  const fetchUser = () => {
    const res = await fetch(`https://randomuser.me/api/?seed=${searchWord}`);
    const data = await res.json();
    setData(data.results);
  };
}, []);

Fixed code:

useEffect(() => {
  const fetchUser = async () => { // Added async keyword
    const res = await fetch(`https://randomuser.me/api/?seed=${searchWord}`);
    const data = await res.json();
    setData(data.results);
  };
  
  fetchUser(); // Added this line to actually call the function
}, [searchWord]); // Added searchWord to the dependency array

Issue 4: localStorage Methods Used Incorrectly

There are two issues with localStorage usage:

Issue in the useState initialization:

const [searchWord, setSearchWord] = useState(
  localStorage.setItem('user') || 'foobar'
);

Fixed code:

const [searchWord, setSearchWord] = useState(
  localStorage.getItem('user') || 'foobar'
);

Issue in the second useEffect:

useEffect(() => {
  localStorage.getItem('user', searchWord);
}, [searchWord]);

Fixed code:

useEffect(() => {
  localStorage.setItem('user', searchWord);
}, [searchWord]);

Issue 5: Missing Cleanup in setInterval useEffect

The interval is created but never cleaned up, which could lead to memory leaks.

Original code:

useEffect(() => {
  const colorInterval = setInterval(() => {
    console.log('i am running');
    setNum((prevNum) => (prevNum === 3 ? 0 : prevNum + 1));
  }, 7000);
}, []);

Fixed code:

useEffect(() => {
  const colorInterval = setInterval(() => {
    console.log('i am running');
    setNum((prevNum) => (prevNum === 3 ? 0 : prevNum + 1));
  }, 7000);
  
  return () => {
    clearInterval(colorInterval); // Added cleanup function
  };
}, []);

Issue 6: Missing Key in User Mapping

The key is specified on the User component but there's another key on the render-wrapper div in User.jsx which is redundant and potentially confusing.

Original code in User.jsx:

return (
  <div className='render-wrapper' key={email}>
    <img src={picture.large} alt={email} />
    <p>
      <span className='name'>
        {name.first} {name.last}
      </span>
    </p>
    <p>{`${location.city}, ${location.state}`}</p>
    <p>{email}</p>
    <button onClick={() => navigate('/')}>Logout</button>
  </div>
);

Fixed code:

return (
  <div className='render-wrapper'> // Removed redundant key
    <img src={picture.large} alt={email} />
    <p>
      <span className='name'>
        {name.first} {name.last}
      </span>
    </p>
    <p>{`${location.city}, ${location.state}`}</p>
    <p>{email}</p>
    <button onClick={() => navigate('/')}>Logout</button>
  </div>
);

Issue 7: Missing Property on data Object

In the map function, we're using data?.map but when the API returns a single user, there's no guarantee that the id will have a value property. We need to use a more reliable key.

Original code:

{data?.map((data) => (
  <User key={data.id.value} data={data} />
))}

Fixed code:

{data?.map((data) => (
  <User key={data.email} data={data} />
))}

Issue 8: Component Not Found Error

Since we've renamed RandomUserTwo to RandomUser, we need to ensure the import in App.jsx matches.

Original import:

import RandomUser from './RandomUser';

This should be fine after fixing the component name in RandomUser.jsx.

Complete Fixed Code

Unmounted.jsx

import { useNavigate } from 'react-router-dom';

const Unmounted = () => {
  const navigate = useNavigate();
  
  return (
    <div className='unmounted'>
      <h1>Welcome</h1>
      <button onClick={() => navigate('/mount')}>Login</button>
    </div>
  );
};

export default Unmounted;

RandomUser.jsx

import { useState, useEffect } from 'react';
import User from './User';

const colors = ['#0c9bbd', 'red', 'orange', 'green'];

const RandomUser = () => {
  const [num, setNum] = useState(0);
  const [searchChange, setSearchChange] = useState('');
  const [searchWord, setSearchWord] = useState(
    localStorage.getItem('user') || 'foobar'
  );
  
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchUser = async () => {
      const res = await fetch(`https://randomuser.me/api/?seed=${searchWord}`);
      const data = await res.json();
      setData(data.results);
    };
    
    fetchUser();
  }, [searchWord]);

  useEffect(() => {
    localStorage.setItem('user', searchWord);
  }, [searchWord]);

  useEffect(() => {
    const colorInterval = setInterval(() => {
      console.log('i am running');
      setNum((prevNum) => (prevNum === 3 ? 0 : prevNum + 1));
    }, 7000);
    
    return () => {
      clearInterval(colorInterval);
    };
  }, []);

  return (
    <div
      style={{
        backgroundColor: colors[num],
        transition: 'background-color 4s'
      }}
      className='container'
    >
      <div className='person'>
        {data?.map((data) => (
          <User key={data.email} data={data} />
        ))}
      </div>
      <div className='form-wrapper'>
        <form
          onSubmit={(e) => {
            e.preventDefault();
            if (searchChange === '') return;
            setSearchWord(searchChange);
            setSearchChange('');
          }}
        >
          <label htmlFor='search'>Search</label>
          <input
            id='search'
            onChange={(e) => setSearchChange(e.target.value)}
            value={searchChange}
            name='searchWord'
            placeholder='Username'
          />
          <button type='submit'>Submit</button>
        </form>
      </div>
    </div>
  );
};

export default RandomUser;

User.jsx

import { useNavigate } from 'react-router-dom';

const User = (props) => {
  const navigate = useNavigate();
  const { name, email, picture, location } = props.data;
  return (
    <div className='render-wrapper'>
      <img src={picture.large} alt={email} />
      <p>
        <span className='name'>
          {name.first} {name.last}
        </span>
      </p>
      <p>{`${location.city}, ${location.state}`}</p>
      <p>{email}</p>
      <button onClick={() => navigate('/')}>Logout</button>
    </div>
  );
};

export default User;

Looking Back

Summary of Fixed Issues

  1. Added missing navigate declaration in Unmounted.jsx
  2. Fixed component name from RandomUserTwo to RandomUser
  3. Properly handled async function in useEffect by adding async keyword and actually calling the function
  4. Fixed localStorage methods (getItem vs setItem)
  5. Added cleanup function for setInterval
  6. Removed redundant key attribute in User component
  7. Changed key in data mapping to use email instead of id.value
  8. Added searchWord to dependency array in first useEffect to fetch new data when search changes

Common useEffect Pitfalls

This exercise highlights some common useEffect issues:

Real-World Applications

Understanding these useEffect patterns is crucial for real-world React applications: