【问题标题】:Remove query from search input still displays results on page从搜索输入中删除查询仍会在页面上显示结果
【发布时间】:2021-09-21 07:56:09
【问题描述】:

我有一个主页,其中显示来自searchContext 的搜索结果。这是我的home page 代码。

import { useSearch } from "./SearchContext";

const Home = () => {
  const { isLoading, search, searchResult } = useSearch();
  const startIndex = Math.floor(Math.random() * searchResult.length - 5);

  return (
    <>
      <h3>Home page</h3>
      {isLoading ? (
        <h1>LOADING...</h1>
      ) : (
        <div>
          <div>Search value: {search}</div>
          {searchResult.slice(startIndex, startIndex + 5).map((el) => (
            <li key={el.id}>{el.title}</li>
          ))}
        </div>
      )}
    </>
  );
};

export default Home;

这是我的searchContext

import { createContext, useEffect, useContext, useState } from "react";

export const SearchContext = createContext({
  isLoading: false,
  search: "",
  searchResult: [],
  setSearch: () => {}
});

export const useSearch = () => useContext(SearchContext);

const SearchProvider = ({ children }) => {
  const [search, setSearch] = useState("");
  const [items, setItems] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const searchNews = async () => {
      console.log(search);
      setIsLoading(true);
      const news = await fetchNews(search, 15, 1);
      setItems(news);
      setIsLoading(false);
    };

    const fetchNews = async (section, pageSize, page) => {
      // const url = `${baseUrl}${section}?page-size=${pageSize}&page=${page}&api-key=${apiKey}`;
      const url = "https://jsonplaceholder.typicode.com/posts";
      const news = await fetch(url);
      return await news.json();
    };
    if (search.length > 0) { // I added this condition so that searchNews is not called when search is empty.
      searchNews();
    } // Here I can add an else statement and call `setItems([])` to set empty array to items but it does not work either.
  }, [search]);

  return (
    <SearchContext.Provider
      value={{
        isLoading,
        search,
        searchResult: items,
        setSearch: (e) => setSearch(e.target.value)
      }}
    >
      {children}
    </SearchContext.Provider>
  );
};

export default SearchProvider;

当我向input field 输入文本时,此上下文一直在调用searchNews。这适用于搜索和显示结果。但是,当我从 input field 中删除 search string 时,我不希望它显示或检索更多 API 调用的结果。现在,如果我从input field 中慢慢删除search string,这将起作用。但是当我越来越快地删除search string 时,items 仍然有结果,它会在home page 上显示这些结果。我不想要这个。我想当search input 为空时,我不想在home page 或任何页面上显示任何search result

为了解决这个问题,我在 searchContext 中添加了一个 if 条件,它检查 search 是否不为空,这意味着 input field 不为空,然后调用 searchNews 方法并从 API 加载结果,但如果它为空然后不要再调用searchNews,它不应该在任何页面上显示search result

这是带有搜索输入字段的 search.js 页面。

import { useSearch } from "./SearchContext";

const Search = () => {
  const { setSearch } = useSearch();

  return (
    <>
      <input
        type="text"
        name="search"
        placeholder="Enter search term"
        onChange={setSearch}
      />
    </>
  );
};

export default Search;

当我越来越快地删除搜索字符串时,如何解决这个问题?

这里是这个问题的代码沙箱。

https://codesandbox.io/s/replace-search-current-page-contents-with-search-contents-forked-x4l0o?file=/src/SearchContext.js

【问题讨论】:

  • 使用 Debounce 怎么样? codesandbox.io/s/…
  • @PsyGik 我检查了你的沙箱,它仍在使用 debounce 显示结果。
  • @Om3ga 请查看我的更新答案。我想这就是你想要的
  • 我不明白。 fetchNews(search.length ? search : "news", 15, 1); 告诉如果不存在搜索术语,则使用默认术语即news 获取。但您也不想显示默认搜索结果?
  • 不应该有这种情况。基本上应该是fetchNews(search, 15, 1);。我更新了沙盒。

标签: javascript reactjs react-context


【解决方案1】:

当我们创建任何自动完成组件时,我们需要考虑用户的打字速度,以及每个字母搜索是否适合这种情况(很可能不是)。

因此,我们可以学习使用 debounce 或 setTimeout 来创建轻微的延迟,同时等待输入,然后再进行获取。

在本例中,创建一个全局变量 timer,它驻留在组件OUTSIDE中,因此它不会在每次渲染时都被调用/重置。

let timer; //outside component


const mycomponent = () => {

.....

  useEffect(() => {
    const searchNews = async () => {

      setIsLoading(true);
      clearTimeout(timer) //clear timer on every call
      timer = setTimeout(() => { //recreate a timer to call the fetch in 100ms
          const news = await fetchNews(search.length ? search : "news", 15, 1);
          setItems(news);
          setIsLoading(false); //need to add try /catch 
      }, 100) //<--- call the function in 100 ms

    };
if (search.length > 0) {
  searchNews();
} else {
  setItems([]);
}
    .............
  }, [search]); 
}

我们将创建计时器,并在每个输入上使用 clearTimeout(timer) 重置计时器。一旦用户暂停输入,您将执行提取。

PS:可以增加100ms的延迟来提高性能。我正在使用 200 毫秒,它适用于我的后端,它在不到 100 毫秒内返回结果。

【讨论】:

  • 当我添加 setTimeout 时,会在短时间内显示主页内容,然后将其替换为 searchResult 内容。
  • 首页内容与 searchResult 内容有什么区别?我在您的原始帖子中看到的逻辑仅显示搜索结果
  • 请注意 setTimeout 函数只获取结果,您的渲染逻辑是分开的,您应该单独查看。
  • 我使用 searchResult 来搜索项目和获取主页项目的信息。主页内容来自本地存储。
  • 是因为你的search.length ?支票吗?在您的代码中,您决定在搜索输入为空时获取“新闻”。如果您想显示您的主页内容,请在搜索输入为空时删除“新闻”请求。事实上,您应该在您的上下文中完全删除这个search.length 逻辑。如果你想默认显示“新闻”,你可以在你的主页组件中使用setSearch("news")
【解决方案2】:

我认为问题在于您正在处理的事件,on change 事件非常适合 select 元素,而 input 元素需要 oninput 事件。 尝试使用 oninput 事件更改 onchange 事件

【讨论】:

    猜你喜欢
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 2021-08-13
    相关资源
    最近更新 更多