React useEffect 警告 Warning: Can't perform a React state update on an unmounted component

React useEffect Warning Warning: Can't perform a React state update on an unmounted component

这个组件有问题。

当我不断调用这个组件时,我收到了一条警告消息。

警告信息如下所示。

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

我已经使用 return 方法清除了 timerId 但我得到了这个文本。

如何修改这个组件中的useEffect?

我该怎么办?

下面是我的组件

import React, { useState, useEffect } from 'react';
import { Form, List, Button } from 'semantic-ui-react';
import axios from 'axios';

const Search = () => {

    const [term, setTerm] = useState('programming')

    const [debouncedTerm, setDebouncedTerm] = useState(term);
    const [results, setResults] = useState([]);

    useEffect(() => {
        const timerId = setTimeout(() => {
          setDebouncedTerm(term);
        }, 1000);
    
        return () => {
          clearTimeout(timerId);
        };
    }, [term]);

    useEffect(() => {
        
        const search = async () => {
            const {data} = await axios.get('https://en.wikipedia.org/w/api.php', {
                params: {
                    action: 'query',
                    list: 'search',
                    origin: '*',
                    format: 'json',
                    srsearch: debouncedTerm,
                },
            });

            setResults(data.query.search);

        };

        
        if(debouncedTerm){
            search();
        }

    }, [debouncedTerm])

    const handleClick = (pageid) => {
        window.open(`https://en.wikipedia.org?curid=${pageid}`);
    }

    const renderedResults = results.map((result) => {
        return (
          <List.Item key={result.pageid}>
                <Button floated='right' style={{"margin": "10px"}} onClick={() => handleClick(result.pageid)}>Go</Button>
                <List.Content>
                    <List.Header>{result.title}</List.Header>
                    {result.snippet}
                </List.Content>
          </List.Item>
        );
    });

    return (
        <React.Fragment>
            <Form> 
                <Form.Field>
                    <label>Search</label>
                    <input placeholder='Search Word' 
                            onChange={(e) => setTerm(e.target.value)}
                            value={term}
                    />
                </Form.Field>
            </Form>

            <List celled>
                {renderedResults}
            </List>
        </React.Fragment>    
    )
}

export default Search;

试试这个 (axios v0.22.0+):

useEffect(() => {
    const controller= new AbortController();
      const search = async () => {
        try{
          const {data} = await axios.get('https://en.wikipedia.org/w/api.php', {
            params: {
                action: 'query',
                list: 'search',
                origin: '*',
                format: 'json',
                srsearch: debouncedTerm,
            },
            signal: controller.signal
          });

        setResults(data.query.search);
      }catch(err){
        if (!axios.isCancel(err)) throw err;
      }

    };

    
    if(debouncedTerm){
       search();
    }

    return ()=>{
       controller.abort();
    }

}, [debouncedTerm])

同样的事情,但具有自动清理功能,可以使用我的库 (Generic fetch demo):

import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";
// ...
useAsyncEffect(function*(){
    if(!debouncedTerm){
       return;
    }
    
    const {data} = yield cpAxios.get('https://en.wikipedia.org/w/api.php', {
            params: {
                action: 'query',
                list: 'search',
                origin: '*',
                format: 'json',
                srsearch: debouncedTerm,
            }
        });

    setResults(data.query.search);     
}, [debouncedTerm])

这可以简化为:

import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";
// ...
const [cancel, done, results, err]= useAsyncEffect(function*(){
    if(!debouncedTerm){
       return;
    }
    
    const {data} = yield cpAxios.get('https://en.wikipedia.org/w/api.php', {
            params: {
                action: 'query',
                list: 'search',
                origin: '*',
                format: 'json',
                srsearch: debouncedTerm,
            }
        });

    return data.query.search;     
}, { states: true, deps: [debouncedTerm] });

console.log('results state:', results);