Passer une fonction en tant que propriété à deux composants renvoie un élément non défini dans le composant cible
P粉818125805
P粉818125805 2024-02-26 10:15:16
0
1
373

import React, { useContext, useState } from 'react';
import Main from '../components/Main';
import Row from '../components/Row';
import requests from '../requests';
import AppContext from '../lib/AuthContext';

const Home = () => {

  const [likedItems, setLikedItems] = useState([]);

  const contextValue = useContext(AppContext);
  const token = window.localStorage.getItem('trailerflix-jwt');

  const handleNewLikes = item => {
    setLikedItems(prevLikedItems => [...prevLikedItems, item]);
  };

  if (token && contextValue.user?.user) {
    return (
      <>
        <Main handleNewLikes={handleNewLikes} />
        <Row rowId='0' title={`${(token && contextValue.user?.user) ? contextValue?.user?.user.username : null}'s List`} fetchURL='/auth/get-likes'
        likedItems={likedItems} handleNewLikes={handleNewLikes} />
        <Row rowId='1' title='Top 10 Movies in the U.S. Today' fetchURL={requests.popular} />
        <Row rowId='2' title='Coming Soon' fetchURL={requests.upcoming} />
        <Row rowId='3' title='Trending Now' fetchURL={requests.trending} />
        <Row rowId='4' title='Now Playing in Theaters' fetchURL={requests.nowPlaying} />
        <Row rowId='5' title='Animation' fetchURL={requests.animation} />
        <Row rowId='6' title='Horror' fetchURL={requests.horror} />
        <Row rowId='7' title='Comedy' fetchURL={requests.comedy} />
      </>
    );
  } else {
    return (
      <>
        <Main />
        <Row rowId='1' title='Top 10 Movies in the U.S. Today' fetchURL={requests.popular} />
        <Row rowId='2' title='Coming Soon' fetchURL={requests.upcoming} />
        <Row rowId='3' title='Trending Now' fetchURL={requests.trending} />
        <Row rowId='4' title='Now Playing in Theaters' fetchURL={requests.nowPlaying} />
        <Row rowId='5' title='Animation' fetchURL={requests.animation} />
        <Row rowId='6' title='Horror' fetchURL={requests.horror} />
        <Row rowId='7' title='Comedy' fetchURL={requests.comedy} />
      </>
    );
  }

};

export default Home;

Il s'agit donc du composant parent et à partir de là, je passe la fonction handleNewLikes comme accessoire à Row.

import axios from 'axios';
import React, { useEffect, useState, useRef } from 'react';
import Media from './Media';
import { MdChevronLeft, MdChevronRight } from 'react-icons/md';

const Row = ({ title, fetchURL, rowId, videos, likedItems, handleNewLikes }) => {

  const [media, setMedia] = useState([]);

  useEffect(() => {
    const token = window.localStorage.getItem('trailerflix-jwt');
    if (token && fetchURL === '/auth/get-likes') {
      axios.get(fetchURL, {
        headers: {
          'Content-Type': 'application/json',
          'X-Access-Token': `${token}`
        }
      })
        .then(res => {
          const flattenedArray = res.data.map(item => item.favoritedItem);
          const newestLikesFirst = flattenedArray.reverse();
          setMedia(newestLikesFirst);
        })
        .catch(error => {
          console.error('Axios GET request failed:', error);
        });
    } else if (fetchURL !== '/auth/get-likes') {
      axios.get(fetchURL)
        .then(response => {
          setMedia(response.data.results);
        })
        .catch(error => {
          console.error('Axios GET request failed:', error);
        });
    }
  }, [fetchURL, likedItems]);

  const validMedia = [];

  for (let i = 0; i < media.length; i++) {
    if (media[i].backdrop_path !== null) {
      validMedia.push(media[i]);
    } else {
      media.splice(i, 1);
    }
  }

  const rowRef = useRef(null);
  const [isMoved, setIsMoved] = useState(false);

  const handleSlider = direction => {
    setIsMoved(true);

    if (rowRef.current) {
      const { scrollLeft, clientWidth } = rowRef.current;

      const scrollTo =
        direction === 'left' ? scrollLeft - clientWidth : scrollLeft + clientWidth;

      rowRef.current.scrollTo({ left: scrollTo, behavior: 'smooth' });
    }

  };

  return (

    <>
      <h2 className='text-white font-bold md:text-2xl p-4 mt-8 mb-3 ml-4'>{title}</h2>
      <div className='relative flex items-center group mb-10 ml-4'>

        <MdChevronLeft className={`text-white bg-transparent left-0 absolute hover:opacity-100 cursor-pointer z-10 hidden invisible lg:visible md:visible group-hover:block ${!isMoved && ' lg:invisible md:invisible'}`} size={60}
          onClick={() => handleSlider('left')}
        />
        <div id={'slider' + rowId} className='w-full h-full overflow-x-scroll whitespace-nowrap scroll-smooth relative scrollbar-hide overflow-y-hidden' ref={rowRef}>
          {validMedia.map((item, id) => {
            return <Media key={id} item={item} rowId={rowId} handleNewLikes={handleNewLikes} likedItems={likedItems}/>;
          })}
        </div>
        <MdChevronRight className='text-white bg-transparent right-0 absolute hover:opacity-100 cursor-pointer z-10 hidden invisible lg:visible md:visible group-hover:block' size={60}
          onClick={() => handleSlider('right')}
        />
      </div>
    </>
  );
};

export default Row;

Il s'agit du composant de ligne dans lequel je transmets la fonction handleNewLikes au média.

import React, { useState, useContext, useEffect } from 'react';
import axios from 'axios';
import { FaHeart, FaRegHeart } from 'react-icons/fa';
import Player from './Player';
import AppContext from '../lib/AuthContext';

const Media = ({ item, rowId, handleNewLikes, likedItems }) => {
  const contextValue = useContext(AppContext);

  const [watchClicked, setWatchClicked] = useState(false);
  const [isLiked, setIsLiked] = useState(false);
  const [key, setKey] = useState('');
  const [playTrailer, setPlayTrailer] = useState(false);
  const [noTrailer, setNoTrailer] = useState(false);

  useEffect(() => {
    handleFavoritesList();
  }, []);

  console.log(handleNewLikes);

  const handleTrailerClick = () => {
    axios
      .get(`https://api.themoviedb.org/3/movie/${item.id}?api_key=${process.env.MOVIEDB_API_KEY}&append_to_response=videos`)
      .then(response => {
        const trailer = response.data.videos.results.find(vid => vid.name === 'Official Trailer');
        if (response.data.videos.results.length === 0) {
          setKey('');
          setPlayTrailer(false);
          setNoTrailer(true);
          document.body.style.overflowY = 'hidden';
        } else if (trailer) {
          setKey(trailer);
          setPlayTrailer(true);
        } else if (!trailer) {
          setKey(response.data.videos.results[0]);
          setPlayTrailer(true);
        }
      });
    setWatchClicked(true);
    document.body.style.overflowY = 'hidden';
  };

  const handleLikes = () => {
    const token = window.localStorage.getItem('trailerflix-jwt');
    if (token && contextValue?.user?.user) {
      axios.post('/auth/likes', item, {
        headers: {
          'Content-Type': 'application/json',
          'X-Access-Token': token
        }
      })
        .then(response => {
          console.log(handleNewLikes);
          handleNewLikes(item);
          setIsLiked(true);
        })
        .catch(error => {
          console.error('Fetch failed during POST', error);
        });
    } else {
      window.alert('You need to be signed in to save a movie!');
    }
  };

  const handleFavoritesList = () => {
    const token = window.localStorage.getItem('trailerflix-jwt');
    if (token && contextValue.user?.user) {
      axios.get('/auth/get-likes', {
        headers: {
          'Content-Type': 'application/json',
          'X-Access-Token': token
        }
      })
        .then(response => {
          const result = response.data;
          const isItemLiked = result.some(obj => obj.favoritedItem.id === item.id);
          setIsLiked(isItemLiked);
        })
        .catch(error => {
          console.error('Fetch failed during GET', error);
        });
    } else {
      setIsLiked(false);
    }
  };

  const truncateString = (str, num) => {
    if (str?.length > num) {
      return str.slice(0, num) + '...';
    } else {
      return str;
    }
  };

  const { title, original_title, media_type, name } = item;

  return (
    <>
      {watchClicked && (
        <Player trailer={key} playTrailer={playTrailer} noTrailer={noTrailer} onClose={() => setWatchClicked(false)} />
      )}
      <div className="w-[200px] sm:w-[300px] lg:w-[400px] inline-block cursor-pointer relative transition duration-200 ease-out p-2 lg:mr-1 sm:mr-2 md:hover:scale-105">
        <img
          className="w-full h-auto block rounded-sm object-cover md:rounded"
          src={`https://image.tmdb.org/t/p/w500/${rowId === '1' || rowId === '4' ? item?.poster_path : item?.backdrop_path}`}
          alt={title || original_title || name || media_type || 'Title Unavailable'}
        />
        <div className="absolute top-0 left-0 w-full h-full hover:bg-black/80 opacity-0 hover:opacity-100 ease-in duration-300 text-white">
          <div className="white-space-normal text-xs md:text-sm lg:text-base font-bold flex justify-center items-center text-center h-full">
            <div className="flex-wrap">
              <p className="mb-2">
                {truncateString(title || original_title || name || media_type || 'Title Unavailable', 35)}
              </p>
              <div>
                <a
                  className="border bg-gray-300 text-black border-gray-300 py-1 px-1 text-xs lg:text-base hover:bg-red-600 hover:border-red-600 hover:text-gray-300 ease-in duration-250"
                  onClick={handleTrailerClick}
                >
                  Watch
                </a>
              </div>
            </div>
          </div>
          <p onClick={() => handleLikes()}>
            {isLiked && contextValue.user?.user
              ? (
                <FaHeart className="absolute top-4 left-4 text-red-600" />
                )
              : (
                <FaRegHeart className="absolute top-4 left-4 hover:text-red-600 ease-in duration-100" />
                )}
          </p>
        </div>
      </div>
    </>
  );
};

export default Media;

Dans les médias, la fonction est tantôt définie dans la console et tantôt indéfinie. De plus, lorsque je clique sur l'icône en forme de cœur et que la fonction handleLikes est déclenchée, la fonction handleNewLikes qu'elle contient apparaîtra toujours comme indéfinie.

C'est ce que je vois dans la console

Merci pour votre aide, je suis bloqué depuis quelques jours lmao

Je sais que je pourrais probablement utiliser une sorte de bibliothèque de gestion d'état, mais cela nécessiterait pas mal de refactorisation, et j'ai l'impression que cela devrait fonctionner, et je ne vois pas pourquoi il y aurait des problèmes, à moins que je ne sois myope. < /p>

P粉818125805
P粉818125805

répondre à tous(1)
P粉762730205

J'ai copié votre code et ajouté quelques journaux pour vous aider à voir ce qui se passe. Vous ne commencez qu'à partir de Home -> MainHome -> Row 0传递了handleNewLikes.

Le reste des lignes est handleNewLikes正确地为undefined parce que vous n'avez pas réussi cette fonction. Consultez ces journaux pour voir ce que je veux dire :

Lorsque nous atteignons la fonction Media组件时,你在调用handleLikes时尝试使用一个undefinedhandleNewLikes.

// Media.tsx line 48
  const handleLikes = () => {
    const token = window.localStorage.getItem("trailerflix-jwt");
    if (token && contextValue?.user?.user) {
      axios
        .post("/auth/likes", item, {
          headers: {
            "Content-Type": "application/json",
            "X-Access-Token": token
          }
        })
        .then((response) => {
          console.log(handleNewLikes);
          // Undefined because not passed in to Row which passes to Media
          handleNewLikes(item); 
          setIsLiked(true);
        })
        .catch((error) => {
          console.error("Fetch failed during POST", error);
        });
    } else {
      window.alert("You need to be signed in to save a movie!");
    }
  };

Voici quelques journaux de Media组件中点击like按钮 :

La solution la plus simple est chez Home中更新Row实现的参数,包括handleNewLikes :

<Row 
  rowId='1' 
  title='Top 10 Movies in the U.S. Today' 
  fetchURL={requests.popular} 
  handleNewLikes={handleNewLikes} 
/>
// 同样适用于row 2、3等...

Voici le journal du clic sur le bouton J'aime après avoir transmis la fonction à Row 1

^ Vous devriez utiliser une sorte de gestion étatique. Vous n'avez pas besoin d'utiliser une bibliothèque pour ce faire. Si l'état n'est pas très complexe, vous pouvez utiliser un contexte React similaire à ce que vous avez fait dans AuthContext.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!