You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Framer Motion搜索框输入异常及滑块样式故障排查

问题修复方案

问题1:搜索框输入时自动关闭、内容消失

原因:SearchBar和Slider组件定义在LandingPage函数内部,每次LandingPage状态更新(比如调用setPanel、setFilteredSongs触发重渲染),这两个组件都会被重新创建,导致输入框失去焦点、内容丢失,同时Slider的引用和状态也会被重置。

修复:将SearchBar和Slider组件移到LandingPage外部,通过props传递所需的状态和方法,避免组件重复创建。

问题2:搜索后Slider样式损坏

原因:

  1. Slider的width状态仅在组件挂载时计算一次,搜索后过滤歌曲数量变化,inner-carousel的滚动宽度改变,但width未更新,导致拖拽约束失效、样式错乱。
  2. 搜索结果对应的item未使用motion.div,和初始状态的item结构不一致,导致样式不统一。
  3. 重复编写两套几乎一样的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 15:27:46