【发布时间】:2022-11-29 00:35:43
【问题描述】:
我正在使用 React 构建电影卡片列表。
在卡片悬停上,会显示一个收藏电影的图标。单击该图标后,电影将被收藏,该图标将被另一个图标替换,表明该电影已被收藏:
相对
问题在于,为了实现这一点,必须重新加载浏览器(尽管 DevTools 中的 React Profiler 显示卡片在其图标被点击后重新呈现)。如何修复它以便在单击后立即替换图标?
<Card />组件:
import React from 'react';
import { selectMovie, isMoviePageOpened } from '../../../main.actions';
import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder';
import { Grid, IconButton } from '@material-ui/core';
import noImage from '../../../images/no-image-available.png';
import { NavLink, useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import useStyles from './Card.styles';
import { RootState } from '../../../store';
import { CardProps } from './Card.types';
import FavoriteIcon from '@material-ui/icons/Favorite';
import { getUserName, isUserLoggedIn as isCurrentUserLoggedIn } from '../../../utils/common.utils';
import { favouriteMovie, removeFavouriteMovie } from '../../../utils/movies.utils';
const posterBaseUrl = 'https://image.tmdb.org/t/p/w300';
const Card: React.FC<CardProps> = ({ card }: CardProps) => {
const dispatch = useDispatch();
const classes = useStyles();
const { pathname } = useLocation();
const favouriteMovies = useSelector((state: RootState) => state.profile.favouriteMovies);
const isProfilePageOpened = String(pathname.split('/').pop()) === 'profile' ? true : false;
const isUserLoggedIn = isCurrentUserLoggedIn();
const isMovieFaved = favouriteMovies.includes(card.id);
const currentUser = getUserName();
const changeIsMovieFavourite = () => {
isMovieFaved ? removeFavouriteMovie(currentUser, card.id) : favouriteMovie(currentUser, card.id);
};
const SetSelectedMovieId = (movieId: number) => {
dispatch(isMoviePageOpened(true));
dispatch(selectMovie(movieId));
};
console.log(card.id, 'isMovieFaved ', isMovieFaved);
return (
<Grid item key={card.id}>
<div className="card-container" onClick={() => SetSelectedMovieId(card.id)}>
<NavLink to={'/movie/' + card.id} data-testid="catalog-card">
<img
className={isProfilePageOpened ? classes.profileCard : classes.primaryCard}
alt={'Poster of ' + card.title}
src={
card.poster_path
? card.poster_path.includes('.jpg')
? posterBaseUrl + card.poster_path
: noImage
: noImage
}
title={card.title}
/>
</NavLink>
<div className="card-details">
<IconButton className="fav-icon" onClick={changeIsMovieFavourite}>
<NavLink to={isUserLoggedIn ? '' : '/login'} className="fav-icon-button">
{isMovieFaved ? <FavoriteIcon /> : <FavoriteBorderIcon />}
</NavLink>
</IconButton>
<div className="vote-average">{card.vote_average}</div>
</div>
</div>
</Grid>
);
};
export default Card;
<CatalogCards />组件迭代<Card />:
import React from 'react';
import { useRef, useEffect } from 'react';
import { Grid, CardMedia } from '@material-ui/core';
import '../../../App.scss';
import loadingSpinner from '../../../images/loading-spinner.gif';
import useIntersectionObserver from '../../../customHooks/useIntersectionObserver';
import { changePageSrolledTill } from '../../../main.actions';
import { fetchAllMovies, Movie } from '../../../services/movies.services';
import { showMoviesAtHomePage } from '../../../main.actions';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Card from '../../GeneralComponents/Card/Card';
import useStyles from './CatalogCards.styles';
import { RootState } from '../../../store';
import { textConstants } from '../../../constants';
const CatalogCards: React.FC = () => {
const loadingRef = useRef<HTMLDivElement | null>(null);
const entry = useIntersectionObserver(loadingRef, {});
const isVisible = !!entry?.isIntersecting;
const dispatch = useDispatch();
const movies = useSelector((state: RootState) => state.movies.homePageMovies);
const searchedMovie = useSelector((state: RootState) => state.movies.searchedMovie);
const pageSrolledTill = useSelector((state: RootState) => state.movies.pageSrolledTill);
const classes = useStyles();
useEffect(() => {
if (isVisible) {
if (pageSrolledTill <= 500) {
dispatch(changePageSrolledTill(pageSrolledTill + 1));
fetchAllMovies(String(pageSrolledTill))
.then(nextPage => {
dispatch(showMoviesAtHomePage([...movies, ...nextPage]));
})
.catch(() => {
dispatch(showMoviesAtHomePage([...movies]));
});
}
}
}, [isVisible]);
return (
<div>
{movies.length > 0 && movies.length < 6 && (
<div className={classes.searchResultsTitle}>{textConstants.MOVIES_FOUND}</div>
)}
{movies.length > 0 ? (
<Grid container className={classes.container}>
{movies
.filter((movie: Movie) => movie.vote_average !== 0)
.map((movie: Movie) => (
<Card key={movie.id} card={movie} />
))}
</Grid>
) : searchedMovie ? (
<div className={classes.noResultsMessage}>{textConstants.TRY_DIFFERENT_PHRASE}</div>
) : (
<CardMedia component="img" image={loadingSpinner} className={classes.loadingSpinner} />
)}
{!searchedMovie && (
<div ref={loadingRef}>{pageSrolledTill <= 500 ? '' : textConstants.ALL_MOVIES_SEEN}</div>
)}
</div>
);
};
export default CatalogCards;
谢谢!
【问题讨论】:
-
favouriteMovie()和removeFavouriteMovie是否正确更新了结构?我觉得你改变了结构,而不是以非变异的方式更新它。这可能会导致视图不更新,因为基础对象仍然具有相同的身份 React 可能认为没有任何改变。你能分享favouriteMovie()和removeFavouriteMovie()的代码吗?
标签: javascript reactjs typescript redux react-hooks