redux 如何添加反跳以响应更改输入

iovurdzv  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(151)

我试图在我的React应用程序中添加去抖动功能,并希望在没有loadash,第三方节点模块等库的情况下完成它。
基本上,在handleSearch中,我只是调度redux操作,它执行对API端点的查询,并在输入中将值绑定到redux状态的pice,然后调用Redux Search 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
            }
...

flvtvl50

flvtvl501#

我创建了一个自定义钩子来封装去抖动。

import { useMemo, useState } from 'react';

const debounce = (fn: any, delay: number) => {
  let timeout = -1;
  return (...args: any[]) => {
    if (timeout !== -1) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(fn, delay, ...args);
  };
};

export function useStateDebounced<T>(initialValue: T, delay: number) {
  const [inputValue, _setInputValue] = useState<T>(initialValue);

  const [debouncedInputValue, setDebouncedInputValue] = useState<T>(
    initialValue
  );

  const memoizedDebounce = useMemo(
    () =>
      debounce((value: T) => {
        setDebouncedInputValue(value);
      }, delay),
    [delay]
  );

  const setInputValue = (value: T | ((prevState: T) => T)) => {
    if (value instanceof Function) {
      _setInputValue((p) => {
        const mutated = value(p);
        memoizedDebounce(mutated);
        return mutated;
      });
    } else {
      _setInputValue(value);
      memoizedDebounce(value);
    }
  };

  return [inputValue, debouncedInputValue, setInputValue] as const;
}

字符串
代码中的用法:

export default function App() {
  const [value, debouncedValue, setValue] = useStateDebounced('', 1000);

  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };

  return (
    <div className="App">
      {debouncedValue}
      <input type="text" value={value} onChange={handleInputChange} />
    </div>
  );
}


View on CodeSandbox
或者只使用https://www.npmjs.com/package/use-debounce

vmpqdwk3

vmpqdwk32#

目前还不清楚如何调用searchMovies(),但我认为最简单的方法是将调度 Package 在一个已去抖动的回调中。

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逻辑移动到一个自定义钩子中。我还添加了屏蔽。由于互联网的复杂性,你不能保证按照请求发出的顺序获得结果。这只是忽略了除了最近的请求之外的所有响应。
使用情况与您预期的差不多。

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

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

7fyelxc5

7fyelxc53#

我见过React Redux项目使用此资源为项目添加去抖动。

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);
    };
};

字符串

相关问题