Karan Mann
Karan Mann

Reputation: 43

React useState not triggering re-render of child component

I cant seem to figure out why my MovieDetailCard component will not re-render when the movie state changes. I am passing in the movie object. When I update the state the log outputs correctly in the useEffect but the MovieDetailsCard never receives the updated object.

    const MovieDetails = () => {
    const [movie, setMovie] = useState({});
    const { id } = useParams();
    const { poster } = useParams();

  useEffect(() => {
    const fetchMovie = async () => {
      const response = await fetch(
        `http://www.randyconnolly.com/funwebdev/3rd/api/movie/movies.php?id=${id}`
      );

      const data = await response.json();

      setMovie({ ...data });
    };

    fetchMovie();
  }, []);

  useEffect(() => {
    console.log(movie); // this successfully outputs when movie updates
  }, [movie]);

  return (
    <div className="row">
      <div className="col s12 m6">
        <MovieDetailsCard poster={poster} movie={movie} /> // this does not update
      </div>
      <div className="col s12 m6">
        <CastCrewCard />
      </div>
    </div>
  );
};

Below is the MovieDeatailsCard. In the useEffect the console.log(movie) always returns null.

const MovieDetailsCard = ({ poster, movie }) => {
  useEffect(() => {
    console.log("in details");

    console.log(movie);
  }, []);
  return (
    <div className="card">
      <div className="card-content">
        <div className="row">
          <div className="col s6">
            <span className="card-title">Movie Title</span>
          </div>
          <div className="col s6 favouriteButton">
            <FavouriteButton className="waves-effect waves-light btn">
              <i className="material-icons">favorite</i>
            </FavouriteButton>
          </div>
        </div>

        <div className="row">
          <div className="col s12 m6">
            <img src={`https://image.tmdb.org/t/p/w342/${poster}.jpg`} alt="" />
          </div>
          <div className="col s12 m6">
            <p>{movie.title}</p>
          </div>
        </div>
      </div>
    </div>
  );
};

export default MovieDetailsCard;

Upvotes: 0

Views: 3799

Answers (3)

Karan Mann
Karan Mann

Reputation: 43

Thanks guys for the input. This seems to be resolved now. before I was setting data by setData(data) but when I changed to setData({...data}) that worked!

Upvotes: 2

samyak tuladhar
samyak tuladhar

Reputation: 11

Are you sure that you are going receive data from API? The website does not shows up. Try this to test the API

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json())
  .then(json => console.log(json)

In the state declaration (useState) it is better to declare all the keys that you are going to receive all from the API

Upvotes: 0

akhouri
akhouri

Reputation: 3155

By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.

refer: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

try updating useEffect dependendecy to include [id]:

 useEffect(() => {
    const fetchMovie = async () => {
      const response = await fetch(
        `http://www.randyconnolly.com/funwebdev/3rd/api/movie/movies.php?id=${id}`
      );

      const data = await response.json();

      setMovie({ ...data });
    };

    fetchMovie();
  }, [id]);


Upvotes: 0

Related Questions