【譯】如何在React Hooks中獲取資料?

玩弄心裡的鬼發表於2019-04-01

原文連結: www.robinwieruch.de/react-hooks…

在本教程中,我想通過state和effect hook來像你展示如何用React Hooks來獲取資料。我將會使用Hacker News的API來獲取熱門的技術文章。你將會實現一個屬於你自己的自定義hook來在你程式的任何地方複用,或者是作為一個npm包釋出出來。

如果你還不知道這個React的新特性,那麼點選React Hooks介紹,如果你想直接檢視最後的實現效果,請點選這個github倉庫

注意:在未來,React Hooks將不會用於React的資料獲取,一個叫做Suspense的特性將會去負責它。但下面的教程仍會讓你去更多的瞭解關於React中的state和effect hook。

用React Hooks去獲取資料

如果你對在React中獲取資料還不熟悉,可以檢視我其他的React獲取資料的文章。它將會引導你通過使用React的class元件來獲取資料,並且還可以和render props或者高階元件一起使用,以及結合錯誤處理和載入狀態。在這篇文章中,我將會在function元件中使用React Hooks來展示這些功能。

import React, { useState } from 'react';

function App() {
  const [data, setData] = useState({ hits: [] });

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;
複製程式碼

這個App元件展示了一個包含很多項的list(hits = Hacker News 文章)。state和state的更新函式來自於state hook中useState的呼叫,它負責管理我們用來渲染list資料的本地狀態,初始狀態是一個空陣列,此時還沒有為其設定任何的狀態。

我們將使用axios來獲取資料,當然你也可以使用其他的庫或者fetch API,如果你還沒安裝axios,你可以在命令列使用npm install axios來安裝它。然後來實現用於資料獲取的effect hook:

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });

  useEffect(async () => {
    const result = await axios(
      'http://hn.algolia.com/api/v1/search?query=redux',
    );

    setData(result.data);
  });

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;
複製程式碼

通過axios在useEffect中獲取資料,然後通過setData將資料放到元件本地的state中,並通過async/await來處理Promise。

然而當你執行程式的時候,你應該會遇到一個討厭的迴圈。effect hook不僅在元件mount的時候也會在update的時候執行。因為我們在每一次的資料獲取之後,會去通過setState設定狀態,這時候元件update然後effect就會執行一遍,這就造成了資料一次又一次的獲取。我們僅僅是想要在元件mount的時候來獲取一次資料,這就是為什麼我們需要在useEffect的第二個引數提供一個空陣列,從而實現只在mount的時候觸發資料獲取而不是每一次update。

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });

  useEffect(async () => {
    const result = await axios(
      'http://hn.algolia.com/api/v1/search?query=redux',
    );

    setData(result.data);
  }, []);

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;
複製程式碼

第二個引數可以定義hooks所依賴的變數(在一個陣列中去分配),如果一個變數改變了,hooks將會執行一次,如果是一個空陣列的話,hooks將不會在元件更新的時候執行,因為它沒有監聽到任何的變數。

這裡還有一個陷阱,在程式碼中,我們使用async/await從第三方的API中獲取資料,根據文件,每一個async函式都將返回一個promise,async函式宣告定義了一個非同步函式,它返回一個asyncFunction物件,非同步函式是通過事件迴圈非同步操作的函式,使用隱式Promise返回其結果。但是,effect hook應該不返回任何內容或清除功能,這就是為什麼你會在控制檯看到以下警告:07:41:22.910 index.js:1452 Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect..這就是為什麼不允許在useEffect函式中直接使用async的原因。讓我們通過在effect內部使用非同步函式來實現它的解決方案。

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        'http://hn.algolia.com/api/v1/search?query=redux',
      );

      setData(result.data);
    };

    fetchData();
  }, []);

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;
複製程式碼

簡而言之,這就是用React Hooks獲取資料。但是,如果你對錯誤處理、載入提示、如何從表單中觸發資料獲取以及如何實現可重用的資料獲取hook感興趣,請繼續閱讀。

如何通過程式設計方式/手動方式觸發hook?

好的,我們在mount後獲取了一次資料,但是,如果使用input的欄位來告訴API哪一個話題是我們感興趣的呢?“Redux”可以作為我們的預設查詢,如果是關於“React”的呢?讓我們實現一個input元素,使某人能夠獲取“Redux”以外的話題。因此,為input元素引入一個新的狀態。

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        'http://hn.algolia.com/api/v1/search?query=redux',
      );

      setData(result.data);
    };

    fetchData();
  }, []);

  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}

export default App;
複製程式碼

目前,這兩個狀態彼此獨立,但現在希望將它們耦合起來,以獲取由input中的輸入來查詢指定的專案。通過下面的更改,元件應該在掛載之後通過查詢詞獲取所有資料。

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        `http://hn.algolia.com/api/v1/search?query=${query}`,
      );

      setData(result.data);
    };

    fetchData();
  }, []);

  return (
    ...
  );
}

export default App;
複製程式碼

還差一部分:當你嘗試在input中輸入一些內容時,在mount之後就不會再獲取任何資料了,這是因為我們提供了空陣列作為第二個引數,effect沒有依賴任何變數,因此只會在mount的時候觸發,但是現在的effect應該依賴query,每當query改變的時候,就應該觸發資料的獲取。

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        `http://hn.algolia.com/api/v1/search?query=${query}`,
      );

      setData(result.data);
    };

    fetchData();
  }, [query]);

  return (
    ...
  );
}

export default App;
複製程式碼

現在每當input的值更新的時候就可以重新獲取資料了。但這又導致了另一個問題:對於input中鍵入的每個字元,都會觸發該效果,並執行一個資料提取請求。如何提供一個按鈕來觸發請求,從而手動hook呢?

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [search, setSearch] = useState('');

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        `http://hn.algolia.com/api/v1/search?query=${query}`,
      );

      setData(result.data);
    };

    fetchData();
  }, [query]);

  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button type="button" onClick={() => setSearch(query)}>
        Search
      </button>

      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}
複製程式碼

現在,effect依賴於於search,而不是隨輸入欄位中變化的query。一旦使用者點選按鈕,新的search就會被設定,並且應該手動觸發effect hook。

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [search, setSearch] = useState('redux');

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        `http://hn.algolia.com/api/v1/search?query=${search}`,
      );

      setData(result.data);
    };

    fetchData();
  }, [search]);

  return (
    ...
  );
}

export default App;
複製程式碼

此外,search的初始值也設定為與query相同,因為元件也在mount時獲取資料,因此結果應反映輸入欄位中的值。但是,具有類似的query和search狀態有點令人困惑。為什麼不將實際的URL設定為狀態而來代替search?

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'http://hn.algolia.com/api/v1/search?query=redux',
  );

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(url);

      setData(result.data);
    };

    fetchData();
  }, [url]);

  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>

      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}
複製程式碼

這就是使用effect hook獲取隱式程式設計資料的情況。你可以決定effect依賴於哪個狀態。一旦在點選或其他effect中設定此狀態,此effect將再次執行。在這種情況下,如果URL狀態發生變化,effect將再次執行以從API獲取資料。

React Hooks和loading

讓我們為資料獲取引入一個載入提示。它只是另一個由state hook管理的狀態。loading被用於在元件中渲染一個loading提示。

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'http://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);

      const result = await axios(url);

      setData(result.data);
      setIsLoading(false);
    };

    fetchData();
  }, [url]);

  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>

      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}

export default App;
複製程式碼

一旦呼叫該effect進行資料獲取(當元件mount或URL狀態更改時發生),載入狀態將設定為true。一旦請求完成,載入狀態將再次設定為false。

React Hooks和錯誤處理

如果在React Hooks中加上錯誤處理呢,錯誤只是用state hook初始化的另一個狀態。一旦出現錯誤狀態,應用程式元件就可以為使用者提供反饋。使用async/await時,通常使用try/catch塊進行錯誤處理。你可以在effect內做到:

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'http://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);

      try {
        const result = await axios(url);

        setData(result.data);
      } catch (error) {
        setIsError(true);
      }

      setIsLoading(false);
    };

    fetchData();
  }, [url]);

  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>

      {isError && <div>Something went wrong ...</div>}

      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}

export default App;
複製程式碼

React在表單中獲取資料

到目前為止,我們只有input和按鈕的組合。一旦引入更多的輸入元素,您可能需要用一個表單元素包裝它們。此外,表單還可以通過鍵盤上的“enter”來觸發。

function App() {
  ...

  return (
    <Fragment>
      <form
        onSubmit={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      {isError && <div>Something went wrong ...</div>}

      ...
    </Fragment>
  );
}
複製程式碼

但是現在瀏覽器在單擊提交按鈕時頁面會重新載入,因為這是瀏覽器在提交表單時的固有行為。為了防止預設行為,我們可以通過event.preventDefault()取消預設行為。這也是在React類元件中實現的方法。

function App() {
  ...

  const doFetch = () => {
    setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
  };

  return (
    <Fragment>
      <form onSubmit={event => {
        doFetch();

        event.preventDefault();
      }}>
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      {isError && <div>Something went wrong ...</div>}

      ...
    </Fragment>
  );
}
複製程式碼

現在,當你單擊提交按鈕時,瀏覽器不會再重新載入。它和以前一樣工作,但這次使用的是表單,而不是簡單的input和按鈕組合。你也可以按鍵盤上的“回車”鍵。

自定義資料獲取hook

為了提取用於資料獲取的自定義hook,請將屬於資料獲取的所有內容,移動到一個自己的函式中。還要確保能夠返回App元件所需要的全部變數。

const useHackerNewsApi = () => {
  const [data, setData] = useState({ hits: [] });
  const [url, setUrl] = useState(
    'http://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);

      try {
        const result = await axios(url);

        setData(result.data);
      } catch (error) {
        setIsError(true);
      }

      setIsLoading(false);
    };

    fetchData();
  }, [url]);

  const doFetch = () => {
    setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
  };

  return { data, isLoading, isError, doFetch };
}
複製程式碼

現在,你可以在App元件中使用新的hook了。

function App() {
  const [query, setQuery] = useState('redux');
  const { data, isLoading, isError, doFetch } = useHackerNewsApi();

  return (
    <Fragment>
      ...
    </Fragment>
  );
}
複製程式碼

接下來,從dofetch函式外部傳遞URL狀態:

const useHackerNewsApi = () => {
  ...

  useEffect(
    ...
  );

  const doFetch = url => {
    setUrl(url);
  };

  return { data, isLoading, isError, doFetch };
};

function App() {
  const [query, setQuery] = useState('redux');
  const { data, isLoading, isError, doFetch } = useHackerNewsApi();

  return (
    <Fragment>
      <form
        onSubmit={event => {
          doFetch(
            `http://hn.algolia.com/api/v1/search?query=${query}`,
          );

          event.preventDefault();
        }}
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      ...
    </Fragment>
  );
}
複製程式碼

初始狀態也可以變為通用狀態。把它簡單地傳遞給新的自定義hook:

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';

const useDataApi = (initialUrl, initialData) => {
  const [data, setData] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);

      try {
        const result = await axios(url);

        setData(result.data);
      } catch (error) {
        setIsError(true);
      }

      setIsLoading(false);
    };

    fetchData();
  }, [url]);

  const doFetch = url => {
    setUrl(url);
  };

  return { data, isLoading, isError, doFetch };
};

function App() {
  const [query, setQuery] = useState('redux');
  const { data, isLoading, isError, doFetch } = useDataApi(
    'http://hn.algolia.com/api/v1/search?query=redux',
    { hits: [] },
  );

  return (
    <Fragment>
      <form
        onSubmit={event => {
          doFetch(
            `http://hn.algolia.com/api/v1/search?query=${query}`,
          );

          event.preventDefault();
        }}
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      {isError && <div>Something went wrong ...</div>}

      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}

export default App;
複製程式碼

這就是使用自定義hook獲取資料的方法。hook本身對API一無所知。它從外部接收所有引數,只管理必要的狀態,如資料、載入和錯誤狀態。它執行請求並將資料作為自定義資料獲取hook返回給元件。

Reducer的資料獲取hook

reducer hook返回一個狀態物件和一個改變狀態物件的函式。dispatch函式接收type和可選的payload。所有這些資訊都在實際的reducer函式中使用,從以前的狀態、包含可選payload和type的action中提取新的狀態。讓我們看看這在程式碼中是如何工作的:

import React, {
  Fragment,
  useState,
  useEffect,
  useReducer,
} from 'react';
import axios from 'axios';

const dataFetchReducer = (state, action) => {
  ...
};

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);

  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });

  ...
};
複製程式碼

Reducer Hook接受reducer函式和一個初始化的狀態物件作為引數,在我們的例子中,資料、載入和錯誤狀態的初始狀態的引數沒有改變,但是它們被聚合到由一個reducer hook管理的一個狀態物件,而不是單個state hook。

const dataFetchReducer = (state, action) => {
  ...
};

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);

  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });

  useEffect(() => {
    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });

      try {
        const result = await axios(url);

        dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
      } catch (error) {
        dispatch({ type: 'FETCH_FAILURE' });
      }
    };

    fetchData();
  }, [url]);

  ...
};
複製程式碼

現在,在獲取資料時,可以使用dispatch向reducer函式傳送資訊。dispatch函式傳送的物件包括一個必填的type屬性和可選的payload。type告訴Reducer函式需要應用哪個狀態轉換,並且Reducer還可以使用payload來提取新狀態。畢竟,我們只有三種狀態轉換:初始化獲取過程,通知成功的資料獲取結果,以及通知錯誤的資料獲取結果。

在自定義hook的最後,狀態像以前一樣返回,但是因為我們有一個狀態物件,而不再是獨立狀態,所以需要用擴充套件運算子返回state。這樣,呼叫useDataApi自定義hook的使用者仍然可以訪問data、isloading和isError:

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);

  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });

  ...

  const doFetch = url => {
    setUrl(url);
  };

  return { ...state, doFetch };
};
複製程式碼

最後,還缺少了reducer函式的實現。它需要處理三種不同的狀態轉換,即FETCH_INIT、FETCH_SUCCESS和FETCH_FAILURE。每個狀態轉換都需要返回一個新的狀態物件。讓我們看看如何用switch case語句實現這一點:

const dataFetchReducer = (state, action) => {
  switch (action.type) {
    case 'FETCH_INIT':
      return { ...state };
    case 'FETCH_SUCCESS':
      return { ...state };
    case 'FETCH_FAILURE':
      return { ...state };
    default:
      throw new Error();
  }
};
複製程式碼

reducer函式可以通過其引數訪問當前狀態和action。到目前為止,switch case語句中的每個狀態轉換隻會返回原來的狀態。...語句用於保持狀態物件不變(意味著狀態永遠不會直接改變),現在,讓我們重寫一些當前狀態返回的屬性,以便在每次狀態轉換時更改狀態:

const dataFetchReducer = (state, action) => {
  switch (action.type) {
    case 'FETCH_INIT':
      return {
        ...state,
        isLoading: true,
        isError: false
      };
    case 'FETCH_SUCCESS':
      return {
        ...state,
        isLoading: false,
        isError: false,
        data: action.payload,
      };
    case 'FETCH_FAILURE':
      return {
        ...state,
        isLoading: false,
        isError: true,
      };
    default:
      throw new Error();
  }
};
複製程式碼

現在,每個狀態轉換(由操作的type決定)都將基於先前的狀態和可選的payload返回一個新的狀態。例如,在成功請求的情況下,payload用於設定新狀態物件的資料。

總之,reducer hook確保狀態管理的這一部分是用自己的邏輯封裝的。通過提供type和可選payload,你將始終已一個可預測的狀態結束。此外,你將永遠不會進入無效狀態。例如,以前可能會意外地將isloading和isError狀態設定為true。在這個案例的使用者介面中應該顯示什麼?現在,reducer函式定義的每個狀態轉換都會導致一個有效的狀態物件。

在effect hook中禁止資料獲取

即使元件已經解除安裝(例如,由於使用react路由器導航而離開),設定元件狀態也是react中的一個常見問題。我以前在這裡寫過這個問題,它描述瞭如何防止在各種場景中為unmount的元件設定狀態。讓我們看看如何防止在自定義hook中為資料獲取設定狀態:

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);

  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });

  useEffect(() => {
    let didCancel = false;

    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });

      try {
        const result = await axios(url);

        if (!didCancel) {
          dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
        }
      } catch (error) {
        if (!didCancel) {
          dispatch({ type: 'FETCH_FAILURE' });
        }
      }
    };

    fetchData();

    return () => {
      didCancel = true;
    };
  }, [url]);

  const doFetch = url => {
    setUrl(url);
  };

  return { ...state, doFetch };
};
複製程式碼

每個effect hook都有一個clean功能,在元件解除安裝時執行。clean函式是從hook返回的一個函式。在我們的例子中,我們使用一個名為didCancel的布林標誌,讓我們的資料獲取邏輯知道元件的狀態(已裝載/未裝載)。如果元件已解除安裝,則標誌應設定為“tree”,這將導致在最終非同步解決資料提取後無法設定元件狀態。

注意:事實上,資料獲取不會中止——這可以通過axios的Cancellation實現——但是對於未安裝的元件,狀態轉換會不再執行。因為在我看來,axios的Cancellation並不是最好的API,所以這個防止設定狀態的布林標誌也能起到作用。


你已經瞭解了在React中state和effect hook如何用於獲取資料。如果您對使用render props和高階元件在類元件(和函式元件)中獲取資料很感興趣,請從一開始就去我的另一篇文章。否則,我希望本文對您瞭解react hook以及如何在現實場景中使用它們非常有用。

相關文章