2

我正在使用 Robin Wieruch 的出色工作来了解如何调用 API 端点。这是他的一个项目:https ://github.com/the-road-to-learn-react/react-hooks-introduction

下图是他的修改版useDataApiHook-example。有了它,我连接了两个按钮,然后给了我 3 种查询端点的方法。我选择在所有情况下都使用相同的端点,但用不同的端点对其进行了测试,它似乎工作正常。

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

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();
  }
};

export const useDataApi = (initialUrl, initialData) => {
  console.log('Starting useDataApi');
  const [url, setUrl] = useState(initialUrl);

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

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

    const fetchData = async () => {
      console.log('Dispatching FETCH_INIT');
      dispatch({ type: 'FETCH_INIT' });

      try {
        const result = await axios(url);

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

    fetchData();

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

  return [state, setUrl];
};


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

  const handleTestButton1Click = (event) => {
    console.log('Test Button 1 pressed');
    doFetch(
      'http://hn.algolia.com/api/v1/search?query=canada',
    );
  };

  const handleTestButton2Click = (event) => {
    console.log('Test Button 2 pressed');
    doFetch(
      'http://hn.algolia.com/api/v1/search?query=germany',
    );
  };

  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>
      <div>
        <button type="button" onClick={() => handleTestButton1Click()}>Test 1</button> &nbsp; &nbsp; 
        <button type="button" onClick={() => handleTestButton2Click()}>Test 2</button> 
      </div>  

      {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;

我有几个问题:

  1. 假设我不想在组件加载时调用查询(并且不想在加载时发出 GET 请求)。如何做到这一点?
  2. 想象一下,按钮 1 和 2 触发的事件实际上分别是对不同 API 端点的调用。例如,按钮 1 可以代表一个调用,以查明刚刚输入的特定用户名是否已经存在。按钮 2 可以代表一个呼叫,以查明刚刚输入的电子邮件地址是否已经存在。在这种情况下,确保正确使用newdata填充的常见做法是什么?doFetch

如果问题 2 不清楚,让我换一种说法:在所有情况下,data对象都由useDataApi. 但是,如果第一次调用useDataApi是为了填充一个东西,第二次调用是为了填充其他东西,第三次调用是为了再次填充其他东西,那么处理这种逻辑的最佳方法是什么?

4

1 回答 1

1

假设我不想在组件加载时调用查询(并且不想在加载时发出 GET 请求)。如何做到这一点?

您可以将一个空字符串传递给您的钩子,并创建一个当 API 端点为空时不会触发 fetch 的条件:

// I've removed initialUrl
export const useDataApi = (initialData) => {
  console.log('Starting useDataApi');
  const [url, setUrl] = useState('');

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

  useEffect(() => {
    if (!url) {
      return;
    }

    let didCancel = false;

    ...

  }, [url]);

  return [state, setUrl];
}

想象一下,按钮 1 和 2 触发的事件实际上分别是对不同 API 端点的调用。例如,按钮 1 可以代表一个调用,以查明刚刚输入的特定用户名是否已经存在。按钮 2 可以代表一个呼叫,以查明刚刚输入的电子邮件地址是否已经存在。在这种情况下,确保正确使用 doFetch 填充的新数据的常见做法是什么?

由你决定。在我看来,处理新数据的高级部分您已经做过:使用减速器。我会考虑填充data为一个对象,其中每个属性都是每个 API 响应的 id。我的意思是,如果所有 API 都返回一个 field type,我会使用它以这种方式保存数据:

const type = ... // get type from payload
const { data } = state;

// if you want to replace old data
data[type] = action.payload;

// if you want to append new data
data[type].push(action.payload)

return {
  ...state,
  isLoading: false,
  isError: false,
  data,
};
于 2019-09-04T10:57:57.917 回答