javascript - 如何添加去抖动以响应 onChange 输入

标签 javascript reactjs typescript redux

我正在尝试向我的 React 应用程序添加去抖动功能,并且希望在没有像 loadash、3rd 方节点模块等库的情况下做到这一点。我尝试了一些帖子,但对我没有任何帮助。
基本上,在 handleSearch我只是调度 redux 操作,该操作执行对 API 端点的查询,并将输入绑定(bind)值绑定(bind)到 redux 状态并调用 handleSearch onChange。
我的代码是:

const handleSearch = (e: React.FormEvent<HTMLInputElement>) => {
        const value = e.currentTarget.value
        dispatch(setSearch(value))
    }
后来作为返回
<input type="text" value={searchQuery} onChange={handleSearch} />
另外,我的行动:
export const searchMovies = (category: String, searchQuery: string) => async (dispatch: Dispatch<ControlDispatchTypes>) => {
    try {
        dispatch({
            type: SEARCH_LIST_LOADING
        })
        let res: any;

        if (searchQuery.length >= 3) {
            res = await axios.get(`https://api.themoviedb.org/3/search/${category}?api_key=xxxxxxxxxx&query=${searchQuery}`)
        }

        dispatch({
            type: SEARCH_LIST_SUCCESS,
            payload: res.data.results
        })

    } catch (error) {
        dispatch({
            type: SEARCH_LIST_FAIL
        })
    }
}
和一 block 用于搜索的 reducer :
...
 case SET_SEARCH:
            return {
                ...state,
                search: action.payload
            }
        case SEARCH_LIST_LOADING:
            return {
                ...state,
                searchLoading: false
            }
        case SEARCH_LIST_SUCCESS:
            return {
                ...state,
                searchLoading: false,
                items: action.payload

            }
        case SEARCH_LIST_FAIL:
            return {
                ...state,
                searchLoading: false
            }
...

最佳答案

不清楚您是如何调用 searchMovies() 的。但我认为做这种事情最简单的方法是将你的调度包装在一个已经去抖动的回调中。

const debounce = (fn, delay) => {
  let timeout = -1;

  return (...args) => {
    if (timeout !== -1) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(fn, delay, ...args);
  };
};

export const App = () => {
  const query = useSelector(selectQuery);
  const dispatch = useDispatch();
  
  const requestMovies = useMemo(() => {
    return debounce((query) => {
      dispatch(searchMovies(query))
    }, 300);
  }, []);

  const onQueryChange = useCallback((q) => {
    dispatch(setSearch(q));
    requestMovies(q);
  }, []);

  return (
    <div className="App">
      <input value={query} onChange={(e) => onQueryChange(e.currentTarget.value)} />
    </div>
  );
}
这就是简单的解决方案。如果您想要奖励积分,请考虑编写一个自定义 Hook 来拉取包含所有状态和逻辑。它清理组件并使电影搜索可重用。
const QUERY_DEBOUNCE_PERIOD = 400;

const useMovieSearch = () => {
  const dispatch = useDispatch();
  const query = useSelector(selectQuery);
  const category= useSelector(selectCategory);
  const fence = useRef("");

  const requestMovies = useMemo(() => {
    const request = async (category, query) => {
      const uri = `https://api.themoviedb.org/3/search/${category}?api_key=xxxxxxxxxx&query=${query}`;
      fence.current = uri;

      dispatch({
        type: SEARCH_LIST_LOADING
      });

      try {
        if (query.length >= 3) {
          const res = await axios.get(uri);

          if (fence.current === uri) {
            dispatch({
              type: SEARCH_LIST_SUCCESS,
              payload: res.data.results
            });
          }
        }
      } catch (error) {
        dispatch({
          type: SEARCH_LIST_FAIL
        })
      }
    };

    return debounce(request, QUERY_DEBOUNCE_PERIOD);
  }, []);

  const searchMovies = useCallback((category, query) => {
    dispatch({ type: SET_SEARCH, payload: query });
    requestMovies(category, query);
  }, [requestMovies]);

  return {
    query,
    category,
    searchMovies
  };
};
这应该看起来像很标准的东西。我刚刚移动了所有 searchMovies逻辑到自定义钩子(Hook)中。我还添加了围栏。由于互联网的异步性,不能保证您按照请求发出的顺序获得结果。这只是忽略了除了最近的请求之外的所有响应。
使用情况几乎是您所期望的。
const SearchBar = () => {
  const [query, category, searchMovies] = useMovieSearch();

  return <input value={query} onChange={(e) => searchMovies(category, e.currentTarget.value)} />;
};

关于javascript - 如何添加去抖动以响应 onChange 输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64579144/

相关文章:

reactjs - 测试去抖动函数 React - React-testing-library

typescript - 使用 Jest 测试 Vuejs 和 TypeScript 会导致错误 "Property ' xxx' does not exist on type 'CombinedVueInstance'

typescript - isArray 检查和推断类型从不在 TypeScript 中

javascript - 从 Angular 中的另一个组件调用模式对话框的正确方法?

javascript - 我如何使用本地存储来存储多个变量的值?

javascript - 如何将 Express js 导入到 React js 中?

javascript - ReactJS:css 转换在 componentDidMount 中不起作用

对象内的javascript空数组

javascript - 在新窗口中打开谷歌地图反向地理编码

javascript - 使用 ECMA 脚本向节点(页面)添加新属性