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

React实时搜索时输入框内容不保留,无法连续搜索求助

React搜索输入框内容丢失问题修复

问题现象

实现电影搜索功能时,输入第一个字符后虽能返回匹配结果,但输入框会被清空,无法连续输入搜索词。例如搜索Casablanca时,输入c能返回含c的电影,但输入框变为空;继续输入a时,仅返回含a的电影而非ca组合的匹配结果。

原代码

const Index = (props) => {
    const [ movies, setMovies ] = useState(null);
    const [ filter, setFilter ] = React.useState("");

    function SearchBar({ onSearch }) {
        const onSub = (e) => {
            onSearch(e.target.value.toLowerCase());
        };
      
        return (
            <form>
                <div>
                    <input
                        onChange={onSub}
                        className="search-input"
                        placeholder="Search"
                        name="search"
                    />
                </div>
            </form>
        );
    }


    function SearchPage() {
        const filteredData = React.useMemo(() => { 
            if (filter === "") return movies;
            return movies.filter((item) =>
                item.primaryTitle.toLowerCase().includes(filter)
            );
        }, []);
        
        const moviesList = filteredData.map((movie) => {
            return  <Grid xs={6} md={4}> 
                        <MovieCard 
                            key={movie._id} 
                            movie={movie} 
                            authenticated={props.authenticated} 
                        /> 
                    </Grid>;
        });

        return (
            <>
                <SearchBar onSearch={(searchTerm) => setFilter(searchTerm)}/>
                <h2>Movies</h2>
                <Grid container spacing={2}>
                { moviesList }
                </Grid>
            </>
        );
    }

    return (
        <>
            <SearchPage />
        </>
    );
};

export default Index;

用户尝试的代码

function SearchBar({ onSearch }) {
        let testing = "";

        const onSub = (e) => {
            onSearch(e.target.value.toLowerCase());
            testing = testing + e.target.value;
            console.log("testing = " + testing)
        };
      
        return (
            <form>
                <div>
                    <input
                        onChange={onSub}
                        className="search-input"
                        placeholder="Search"
                        name="search"
                        value={testing}
                    />
                </div>
            </form>
        );
    }

问题根源

  1. 组件重复定义导致重新挂载:SearchBar和SearchPage定义在Index组件内部,每次Index状态更新(如调用setFilter),这两个组件会被重新创建,触发卸载再挂载,输入框临时状态直接丢失。
  2. 输入框非受控且状态未同步:输入框未绑定受控状态,父组件的filter值未回传给输入框,导致输入内容无法保留。
  3. useMemo依赖缺失:filteredData的useMemo依赖项为空数组,不会随filter或movies变化重新计算,搜索结果无法实时更新。
  4. 表单默认提交行为未阻止:未阻止表单默认提交,可能触发页面刷新(虽非当前直接问题,但需规避)。

修复方案

1. 移出内部组件到外部

将SearchBar和SearchPage移到Index组件外部,避免每次渲染重新创建组件:

// 独立定义SearchBar组件
function SearchBar({ onSearch, value }) {
    const handleChange = (e) => {
        const inputValue = e.target.value;
        onSearch(inputValue.toLowerCase());
    };

    // 阻止表单默认提交行为
    const handleSubmit = (e) => {
        e.preventDefault();
    };
  
    return (
        <form onSubmit={handleSubmit}>
            <div>
                <input
                    onChange={handleChange}
                    className="search-input"
                    placeholder="Search"
                    name="search"
                    value={value} // 绑定受控值,与父组件状态同步
                />
            </div>
        </form>
    );
}

// 独立定义SearchPage组件,接收必要props
function SearchPage({ movies, filter, authenticated, onSearch }) {
    const filteredData = React.useMemo(() => { 
        if (!movies || filter === "") return movies;
        return movies.filter((item) =>
            item.primaryTitle.toLowerCase().includes(filter)
        );
    }, [movies, filter]); // 添加依赖项,确保结果实时更新
  
    // 处理movies未加载完成的情况,避免map报错
    if (!movies) return <div>Loading...</div>;

    const moviesList = filteredData.map((movie) => {
        return  <Grid xs={6} md={4} key={movie._id}> 
                    <MovieCard 
                        movie={movie} 
                        authenticated={authenticated} 
                    /> 
                </Grid>;
    });

    return (
        <>
            <SearchBar onSearch={onSearch} value={filter}/>
            <h2>Movies</h2>
            <Grid container spacing={2}>
                { moviesList }
            </Grid>
        </>
    );
}

2. 修改Index组件传递props

const Index = (props) => {
    const [ movies, setMovies ] = useState(null);
    const [ filter, setFilter ] = React.useState("");

    // 这里可添加获取电影数据的逻辑,示例:
    // useEffect(() => {
    //   fetch('/api/movies').then(res => res.json()).then(data => setMovies(data));
    // }, []);

    return (
        <>
            <SearchPage 
                movies={movies} 
                filter={filter} 
                authenticated={props.authenticated}
                onSearch={setFilter}
            />
        </>
    );
};

export default Index;

关键说明

  • 受控组件绑定:SearchBar输入框通过value={filter}绑定父组件状态,确保输入内容与filter同步,不会丢失。
  • 组件稳定性:外部定义的组件不会随父组件渲染重新创建,保留内部状态。
  • useMemo依赖:添加movies和filter作为依赖,确保过滤结果随输入实时更新。
  • 表单提交阻止:通过onSubmit阻止默认行为,避免意外页面刷新。

内容的提问来源于stack exchange,提问作者Marcus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:25:00