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

Joh*_*cks 5 typescript reactjs

我创建了一个API端点,当用户尝试搜索时返回产品标题。现在在前端,当在输入字段上输入一些按键时,我将对该端点进行 API 调用。所以我将该组件编写React为基于类的组件。它工作正常。但是现在我想React通过使用React钩子在较新版本中转换该组件。

的基于类的实现工作正常。我所做的是当用户输入一些按键时。我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"
  }
]
Run Code Online (Sandbox Code Playgroud)

基于类的实现:

//#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
Run Code Online (Sandbox Code Playgroud)

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

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

钩子实现:

//#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
Run Code Online (Sandbox Code Playgroud)

API调用演示

Dr4*_*ass 2

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