【问题标题】:Get axios responses in the same order as requests for search functionality以与搜索功能请求相同的顺序获取 axios 响应
【发布时间】:2021-10-27 01:24:52
【问题描述】:

我目前正在使用 axios 在 React Native 中开发搜索功能。

在实现搜索功能时,我使用 debounce from lodash 来限制发送的请求数量。

但是,由于请求响应的接收顺序不同,因此可能会显示错误的搜索结果。

例如,当用户在输入字段中输入“家居装饰”时,会有两个请求。

一个带有 'Home' 的请求,下一个带有 'Home deco' 作为搜索查询文本的请求。

如果带有 'Home' 的请求比第二个请求花费更多的时间返回,我们最终将显示 'Home' 查询文本而不是 'Home deco 的结果'

如果响应按顺序返回,但如果 'Home' 请求在 'Home deco' 请求之后返回,则两个结果都应按顺序显示给用户,那么 'Home' 响应应该被忽略。

以下是示例代码

function Search (){
    const [results, setResults] = useState([]);
    const [searchText, setSearchText] = useState('');

    useEffect(() => {
            getSearchResultsDebounce(searchText);
    }, [searchText]);

    const getSearchResultsDebounce = useCallback(
        _.debounce(searchText => {
            getSearchResults(searchText)
        }, 1000),
        []
    );

    function getSearchResults(searchText) {

        const urlWithParams = getUrlWithParams(url, searchText);
        axios.get(urlWithParams, { headers: config.headers })
             .then(response => {
              if (response.status === 200 && response.data) 
              {
                setResults(response.data);

              } else{
                  //Handle error
              }
            })
            .catch(error => {
                //Handle error
            });
    }

    return (
     <View>
        <SearchComponent onTextChange={setSearchText}/>
        <SearchResults results={results}/>
     </View>
    )

}

解决上述问题的最佳方法是什么?

【问题讨论】:

  • 在类似的情况下,我检查了查询文本,如果查询与结果匹配,则显示匹配的结果,否则仅缓存。
  • 在以正确顺序返回响应的情况下,它会阻止我们在我的示例中显示“Home”的结果?
  • 你能展示你的实现吗?实现这一目标的方法有很多。
  • 承诺链 - 可选地与请求取消结合
  • 不幸的是,我真的不擅长react ...但是,一般来说,我会这样做是有两个变量...sequencedisplayedSequence ...发出请求时,您有一个关联的mySequence = ++sequence ...收到响应时,您检查mySequence 是否小于displayedSequence - 如果是,则setresults 并将displayedSequence 设置为@987654330 @ - 对不起,我不能为你写代码,正如我所说,我的react 很弱

标签: javascript ajax react-native axios


【解决方案1】:

如果你想避免使用外部库来减小包大小,比如axios-hooks,我认为你最好使用 axios 中包含的 CancelToken 功能。

正确使用 CancelToken 功能还可以防止任何警告因未能取消异步任务而做出反应。

Axios 有一个很好的页面来解释如何使用 CancelToken 功能here。如果您想更好地了解它的工作原理以及它为何有用,我建议您阅读。

在您给出的示例中,我将如何实现 CancelToken 功能:

OP 在回复中澄清说他们不想实现取消功能,在这种情况下,我会使用如下时间戳系统:

function Search () {
    //change results to be a object with 2 properties, timestamp and value, timestamp being the time the request was issued, and value the most recent results
    const [results, setResults] = useState({
        timeStamp: 0,
        value: [],
    });
    const [searchText, setSearchText] = useState('');

    //create a ref which will be used to store the cancel token
    const cancelToken = useRef();
   
    //create a setSearchTextDebounced callback to debounce the search query
    const setSearchTextDebounced = useCallback(
        _.debounce((text) => {
            setSearchText(text)
        ), [setSearchText]
    );
   
    //put the request inside of a useEffect hook with searchText as a dep
    useEffect(() => {
        //generate a timestamp at the time the request will be made
        const requestTimeStamp = new Date().valueOf();

        //create a new cancel token for this request, and store it inside the cancelToken ref
        cancelToken.current = CancelToken.source();            
        
        //make the request
        const urlWithParams = getUrlWithParams(url, searchText);
        axios.get(urlWithParams, { 
            headers: config.headers,

            //provide the cancel token in the axios request config
            cancelToken: source.token 
        }).then(response => {
            if (response.status === 200 && response.data) {
                //when updating the results compare time stamps to check if this request's data is too old
                setResults(currentState => {
                    //check if the currentState's timeStamp is newer, if so then dont update the state
                    if (currentState.timeStamp > requestTimeStamp) return currentState;
                  
                    //if it is older then update the state
                    return {
                        timeStamp: requestTimeStamp,
                        value: request.data,
                    };
                });
            } else{
               //Handle error
            }
        }).catch(error => {
            //Handle error
        });
        
        //add a cleanup function which will cancel requests when the component unmounts
        return () => { 
            if (cancelToken.current) cancelToken.current.cancel("Component Unmounted!"); 
        };
    }, [searchText]);

    return (
        <View>
            {/* Use the setSearchTextDebounced function here instead of setSearchText. */}
            <SearchComponent onTextChange={setSearchTextDebounced}/>
            <SearchResults results={results.value}/>
        </View>
    );
}

如您所见,我还更改了搜索本身的去抖动方式。我在 searchText 值本身去抖动的地方更改了它,并且当 searchText 值更改时运行带有搜索请求的 useEffect 挂钩。这样我们就可以取消之前的请求,运行新的请求,并在同一个钩子中卸载时进行清理。

我修改了我的响应以希望实现 OP 想要发生的事情,同时还包括在组件卸载时正确取消响应。

【讨论】:

  • 如果您阅读了 cmets,OP 不想取消 - 我明白为什么 - 因为在开始新请求之前取消之前的请求会导致“延迟用户体验”
  • 我修改了我的请求以实现我认为 OP 想要发生的事情。不过,我的方法可能是错误的。
  • 这就是我的想法(不知道反应让我退缩了)+1
【解决方案2】:

我们可以这样做来获得最新的 api 响应。

function search() {
    ...
    const [timeStamp, setTimeStamp] = "";
    ...


    function getSearchResults(searchText) {

        //local variable will always have the timestamp when it was called
    const reqTimeStamp = new Date().getTime();

        //timestamp will update everytime the new function call has been made for searching. so will always have latest timestampe of last api call
    setTimeStamp(reqTimeStamp)

    axios.get(...)
        .then(response => {

            // so will compare reqTimeStamp with timeStamp(which is of latest api call) if matched then we have got latest api call response 
            if(reqTimeStamp === timeStamp) {
                return result; // or do whatever you want with data
            } else {

            // timestamp did not match
            return ;
            }

        })
        
     }

}

【讨论】:

  • 正如他所说:'两个结果都应该按顺序显示给用户,如果按顺序返回响应,但如果在'Home deco'请求之后返回'Home'请求,那么'Home'响应应该是忽略。我认为这会实现对吗?
  • 他是说如果在“Home”之前返回“Home deco”请求,则应该忽略“Home”响应(因此,如果我们仍然忽略,则无需支付)。所以最新的时间戳将被匹配
  • 好吧 reqtimestamp 是我所说的局部变量..所以对于每个方法调用它应该是不同的......而
  • 是的,这很复杂,他说如果我没记错就忽略
  • 时间戳用于存储api调用的准确时间
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
相关资源
最近更新 更多