Maison > Questions et réponses > le corps du texte
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粉7627302052024-02-27 09:48:52
J'ai copié votre code et ajouté quelques journaux pour vous aider à voir ce qui se passe. Vous ne commencez qu'à partir de Home -> Main
和Home -> 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
时尝试使用一个undefined
的handleNewLikes
.
// 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
.