React+Framer Motion搜索框输入异常及滑块样式故障排查
问题修复方案
问题1:搜索框输入时自动关闭、内容消失
原因:SearchBar和Slider组件定义在LandingPage函数内部,每次LandingPage状态更新(比如调用setPanel、setFilteredSongs触发重渲染),这两个组件都会被重新创建,导致输入框失去焦点、内容丢失,同时Slider的引用和状态也会被重置。
修复:将SearchBar和Slider组件移到LandingPage外部,通过props传递所需的状态和方法,避免组件重复创建。
问题2:搜索后Slider样式损坏
原因:
- Slider的
width状态仅在组件挂载时计算一次,搜索后过滤歌曲数量变化,inner-carousel的滚动宽度改变,但width未更新,导致拖拽约束失效、样式错乱。 - 搜索结果对应的item未使用
motion.div,和初始状态的item结构不一致,导致样式不统一。 - 重复编写两套几乎一样的carousel结构,冗余且易出错。
修复:
- 给Slider的
useEffect添加依赖项,当过滤后的歌曲列表变化时重新计算宽度。 - 统一两种状态下的item结构,都使用
motion.div。 - 简化条件渲染逻辑,只切换数据源,无需重复编写整个carousel框架。
修改后的完整代码
LandingPage.js
import React, { useState, useEffect, useRef } from 'react' import { motion } from 'framer-motion' import songs from './Songs' import './style.css' // 移至外部,避免每次重渲染重建组件 function SearchBar(props) { return ( <div className="input-group input-group-lg" style={{ width: '700px' }}> <span className="input-group-text" id="inputGroup-sizing-lg">Search Music </span> <input type="text" className="form-control" onChange={props.manageSearch} /> </div> ) } // 移至外部,通过props接收数据源 function Slider({ currentSongs }) { const carousel = useRef(); const [width, setWidth] = useState(0); useEffect(() => { const updateWidth = () => { if (carousel.current) { setWidth(carousel.current.scrollWidth - carousel.current.offsetWidth) } } updateWidth(); // 监听窗口尺寸变化,确保宽度计算准确 window.addEventListener('resize', updateWidth); return () => window.removeEventListener('resize', updateWidth); }, [currentSongs]) // 依赖currentSongs,数据变化时重新计算宽度 return ( <div> <motion.div ref={carousel} className='carousel'> <motion.div className='inner-carousel' drag='x' dragConstraints={{ right: 0, left: -width, }}> {currentSongs.map(song => ( <motion.div className='item d-flex flex-column rounded-top mx-2' key={song.id}> <img src={song.image} alt='' /> <div> <h4 className='text-center'>{song.name}</h4> <p className='text-center'><a href='#' className='text-center'>{song.author}</a></p> </div> </motion.div> ))} </motion.div> </motion.div> </div> ) } export default function LandingPage() { const [filteredSongs, setFilteredSongs] = useState([...songs]) const handleSearchBar = (e) => { const searchword = e.target.value; const filteredword = songs.filter(song => song.name.includes(searchword)) if (searchword.length === 0) { setFilteredSongs([...songs]) } else { setFilteredSongs(filteredword) } } return ( <div className='bg-secondary landingpage d-flex align-items-center justify-content-sm-around flex-column' style={{ width: '100vw', height: '100vh' }}> <SearchBar manageSearch={handleSearchBar} /> {/* 直接传递当前显示的歌曲数组,移除冗余的panel状态 */} <Slider currentSongs={filteredSongs} /> </div> ) }
额外优化点
- 移除了冗余的
panel状态,直接通过filteredSongs控制显示内容,简化逻辑。 - 将原代码中混用的
class改为React推荐的className。 - 添加窗口resize监听,确保Slider宽度在窗口尺寸变化时也能正确计算。
内容的提问来源于stack exchange,提问作者The Content Club
相关产品推荐
相关产品推荐

