reactjs - React 组件正在进行无限的 API 调用

标签 reactjs typescript

我创建了一个 API 端点,当用户尝试搜索时,它会返回产品标题。现在在前端,当在输入字段中输入一些按键时,我将对该端点进行 API 调用。因此,我在 React 中将该组件编写为基于类的组件。效果很好。但现在我想通过使用 React Hook 在新版本的 React 中转换该组件。

我的基于类实现工作正常。我所做的是当用户输入一些按键时。 I debounce 即延迟作为参数传递的函数的执行。该函数是 handleSearchChange(),它从字段中获取值并检查 value 字符串是否大于 1 个字符,然后在指定的延迟后进行 API 调用,该调用作为响应返回一些结果。

服务器从以下数据中过滤结果:

[
  {
    "title": "Cummings - Nikolaus",
    "description": "Assimilated encompassing hierarchy",
    "image": "https://s3.amazonaws.com/uifaces/faces/twitter/michalhron/128.jpg",
    "price": "$20.43"
  },
  {
    "title": "Batz, Kiehn and Schneider",
    "description": "Public-key zero tolerance portal",
    "image": "https://s3.amazonaws.com/uifaces/faces/twitter/attacks/128.jpg",
    "price": "$58.97"
  },
  {
    "title": "Borer, Bartell and Weber",
    "description": "Programmable motivating system engine",
    "image": "https://s3.amazonaws.com/uifaces/faces/twitter/craighenneberry/128.jpg",
    "price": "$54.51"
  },
  {
    "title": "Brekke, Mraz and Wyman",
    "description": "Enhanced interactive website",
    "image": "https://s3.amazonaws.com/uifaces/faces/twitter/vaughanmoffitt/128.jpg",
    "price": "$53.28"
  },
  {
    "title": "Willms and Sons",
    "description": "Compatible next generation superstructure",
    "image": "https://s3.amazonaws.com/uifaces/faces/twitter/madcampos/128.jpg",
    "price": "$49.82"
  }
]

基于类的实现:

//#region Global imports
import React, { Component, ReactElement } from 'react';
import _ from 'lodash';
import axios from 'axios';
//#endregion Global imports


//#region Types
type Data = object;

type StateType = {
    isLoading: boolean;
    results: Data[];
    value: string | undefined;
}
//#endregion Types

//#region Component
const initialState = {
    isLoading: false,
    results: [],
    value: '',
};

export class SearchInputV1 extends Component<{}, StateType> {

    // React component using ES6 classes no longer autobind `this` to non React methods.
    constructor(props: Readonly<{}>) {
        super(props);
        this.state = initialState;
        this.getSearchResults = this.getSearchResults.bind(this);
        this.handleSearchChange = this.handleSearchChange.bind(this);
    }

    // Function to make an API call
    async getSearchResults() {
        try {
            const { value } = this.state;
            const { data } = await axios.get(`http://localhost:3000/api/products?q=${value}`);
            this.setState(prevState => ({ ...prevState, isLoading: false, results: data }));
        } catch (e) {
            console.error(e);
        }
    }

    handleSearchChange(event: React.ChangeEvent<HTMLInputElement>) {

        const { target } = event;

        const val = target.value;
        this.setState(prevState => ({ ...prevState, isLoading: true, value: val }));
        console.log('Method debounce : Type value is : ', val);
        setTimeout(() => {
            const { value } = this.state;
            if (typeof value === 'string' && value.length < 1) {
                return this.setState(prevState => ({ ...prevState, ...initialState }));
            }
              // Makes an API call              
              this.getSearchResults();
        }, 300);
    };


    render(): ReactElement<any> {
        const { value, results } = this.state;
        return (
            <div>
                <label htmlFor="search"/>
                <input type="text" value={value} id="search" name="query"
                       onChange={_.debounce(this.handleSearchChange, 500, { leading: true })}/>

                <div>
                    {results.map((element, index) => {
                        // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
                        // @ts-ignore
                        return <p key={index}>{element.title}</p>;
                    })}
                </div>
            </div>

        );
    }
}


//#endregion Component

现在问题是,在我的 React 钩子(Hook)实现中,当我进行 API 调用时,它永远不会停止对服务器进行无限的 API 调用。

我做错了什么以及如何解决它?

钩子(Hook)实现:

//#region Global imports
import React, { useState, useEffect } from 'react';
import _ from 'lodash';
import axios from 'axios';
//#endregion Global imports


//#region Types
type Data = object;

type StateType = {
    isLoading: boolean;
    results: Data[];
    value: string | undefined;
}
//#enregion Types


//#region Component
const initialState = {
    isLoading: false,
    results: [],
    value: '',
};


export const SearchInputV2 = () => {

    const [state, setState] = useState<StateType>(initialState);

    // Whenever state will be change useEffect will trigger.
    useEffect(() => {
        const getSearchResults = async () => {
            try {
                const { value } = state;
                const { data } = await axios.get(`http://localhost:3000/api/products?q=${value}`);
                setState(prevState => ({ ...prevState, isLoading: false, results: data }));
            } catch (e) {
                console.error(e);
            }
        };
        // After the specified delay makes an API call
        const timer = setTimeout(() => {
            const { value } = state;
            if (typeof value === 'string' && value.length < 1) {
                return setState(prevState => ({ ...prevState, ...initialState }));
            }
            // Makes an API call
            getSearchResults();
        }, 300);

        // This will clear Timeout when component unmont like in willComponentUnmount
        return () => {
            clearTimeout(timer);
        };
    }, [state]);

    const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        const {target} = event;
        const val = target.value;
        setState(prevState => ({ ...prevState, isLoading: true, value: val }));
        console.log('Method debounce : Type value is : ', val);
    };

    const { value, results } = state;

    return (
        <div>
            <label htmlFor="search-v"/>
            <input type="text" value={value} id="search-v" name="query"
                   onChange={_.debounce(handleSearchChange, 500, { leading: true })}/>

            <div>
                {results.map((element, index) => {
                    // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
                    // @ts-ignore
                    return <p key={index}>{element.title}</p>;
                })}
            </div>
        </div>
    );


};

//#endregion Component

Demo of the API call

最佳答案

在您的 useEffect(()=>{}, []) 中。 [] 意味着每次括号内的内容发生变化时,它都会运行 useEffect 内的函数。在您的状态下,每次出现新结果时都会运行效果,效果每次都会得到新结果,从而导致无限调用。请改用[state.value]。但在我看来,最好将它们分开 [value, setValue] = useState(''), [isLoading, setIsLoading] = useState(false), [结果,setResult] = useState([])。所以你可以有 useEffect(()=>{}, [value])

关于reactjs - React 组件正在进行无限的 API 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59233466/

相关文章:

javascript - 如何在 React js 中将状态从一个组件传递到另一个组件?

javascript - 如何在 Material UI 中删除自动完成之外的选定芯片

javascript - 从 Canvas 中删除上下文弧

javascript - 使用 Angular (2) routerLinkActive 指令进行变形路由

reactjs - 使用 useRef Hook "element.current"在 useEffect Hook 中为 null

javascript - 动画化 <Circle/> 的 react-native-svg 短划线长度

javascript - 如何让systemjs从全局格式的CDN加载模块?

javascript - 从响应中获取数组而不是对象

typescript - 泛型:TS2345 'T[keyof T]' 类型的参数不可分配给 'Date' 类型的参数,其中 <T extends {}>

javascript - 如何在 createSlice 的 reducer 中获取状态值?