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;
Das ist also die übergeordnete Komponente und von hier aus übergebe ich die handleNewLikes-Funktion als Requisite an 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;
Dies ist die Zeilenkomponente, in der ich die handleNewLikes-Funktion an die Medien übergebe.
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;
In Medien ist die Funktion manchmal in der Konsole definiert und manchmal undefiniert. Wenn ich außerdem auf das Herzsymbol klicke und die Funktion „handleLikes“ ausgelöst wird, wird die darin enthaltene Funktion „handleNewLikes“ immer als undefiniert angezeigt.
Das sehe ich in der Konsole
Danke für deine Hilfe, ich stecke schon seit ein paar Tagen fest, lmao
Ich weiß, dass ich wahrscheinlich eine Art Zustandsverwaltungsbibliothek verwenden könnte, aber das würde einiges an Refactoring erfordern, und ich habe das Gefühl, dass das funktionieren sollte, und ich verstehe nicht, warum es irgendwelche Probleme geben würde, es sei denn, ich bin es kurzsichtig. < /p>
我复制了你的代码,并添加了一些日志来帮助你看到发生了什么。你只从
Home -> Main
和Home -> Row 0
传递了handleNewLikes
。其余的行的
handleNewLikes
正确地为undefined
,因为你没有传递这个函数。查看这些日志以了解我的意思:当我们到达
Media
组件时,你在调用handleLikes
时尝试使用一个undefined
的handleNewLikes
函数。这是在
Media
组件中点击like按钮
时的一些日志:最简单的解决方案是在
Home
中更新Row
实现的参数,包括handleNewLikes
:这是在将函数传递给
Row 1
后点击like按钮的日志^ 你应该使用某种状态管理。你不需要使用一个库来做这个。如果状态不是非常复杂,你可以使用类似于你在
AuthContext
中所做的React上下文。