使用UseEffect呼叫端点

时间:2019-09-02 03:56:43

标签: react-hooks use-effect

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

下面显示的是他的useDataApiHook-example的修改版本。通过它,我连接了两个按钮,然后为我提供了三种查询端点的方法。我选择在所有情况下都使用相同的端点,但是已经在不同的端点上对其进行了测试,并且看起来工作正常。

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可以代表一个呼叫,以查明是否刚刚输入的电子邮件地址已经存在。在这种情况下,通常的做法是确保正确使用由data填充的新doFetch

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

1 个答案:

答案 0 :(得分:1)

  

假设我不想在组件加载时调用查询(也不想在加载时发出GET请求)。那怎么办?

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

// 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都返回一个字段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,
};