如何添加去抖动以对onChange输入做出反应

时间:2020-10-28 18:27:39

标签: javascript reactjs typescript redux

我正试图在我的React应用程序中添加去抖动功能,并且希望在没有诸如loadash,第三方节点模块等库的情况下进行。我尝试了一些帖子,但对我没有任何帮助。

基本上,在handleSearch中,我只是调度了redux操作,该操作对API端点执行查询,并在输入绑定值中对redux状态的pice进行调用,并调用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
        })
    }
}

和用于搜索的减速器

...
 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
            }
...

2 个答案:

答案 0 :(得分:0)

我已经看到React Redux项目使用此资源为项目添加去抖动功能。它取自https://davidwalsh.name/javascript-debounce-function

export const debounce = (func, wait, immediate) => {
    let timeout;
    return function () {
        const later = () => {
            timeout = null;
            if (!immediate) func.apply(this, arguments);
        };
        const callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(this, arguments);
    };
};

答案 1 :(得分:0)

尚不清楚您如何调用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>
  );
}

那是简单的解决方案。如果您想获得加分,请考虑编写一个自定义钩子以包含所有状态和逻辑。它可以清理组件并使电影搜索可重复使用。

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逻辑移到了一个自定义钩子中。我还添加了围栏。由于Internet的异步性,因此不能保证您以发送请求的顺序获得结果。这只会忽略除最近的请求以外的所有响应。

用法几乎就是您所期望的。

const SearchBar = () => {
  const [query, category, searchMovies] = useMovieSearch();

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