使用钩子获取数据

IT技术 javascript reactjs react-hooks
2021-05-18 01:32:32

我很难用最好的方式从 API 中获取数据一次(以及第一次请求时),然后存储结果以供重用和/或其他组件功能。下面是一个带有useContextand的工作示例useReducer,但对于这样一个“简单”的任务来说是相当复杂的。

有没有更好的方法?以及为大量 API 调用实现这一点的最佳方法是什么。任何建议都非常感谢。

import React from "react";
import ReactDOM from "react-dom";
import axios from 'axios';

const initialState = {
  items: [],
  itemsLoading: false
};

const actions = {
  FETCHING_ITEMS: "FETCHING_ITEMS",
  FETCHED_ITEMS: "FETCHED_ITEMS"
};

const reducer = (state, action) => {
  switch (action.type) {
    case actions.FETCHING_ITEMS:
      return { ...state, itemsLoading: true, items: {} };

    case actions.FETCHED_ITEMS:
      return { ...state, items: action.value };
    default:
      return state;
  }
};

const Context = React.createContext();

const Provider = ({ children }) => {
  const [state, dispatch] = React.useReducer(reducer, initialState);

  const value = {
    items: state.items,
    itemsLoading: state.itemsLoading,
    fetchItems: () => {
      if (!state.itemsLoading) {
        dispatch({ type: actions.FETCHING_ITEMS });
        axios
          .get("https://jsonplaceholder.typicode.com/todos")
          .then(function(response) {
            dispatch({ type: actions.FETCHED_ITEMS, value: response.data });
          });
      }
    }
  };

  return <Context.Provider value={value}>{children}</Context.Provider>;
};

const Filters = () => {
  const { items, fetchItems } = React.useContext(Context);

  React.useEffect(() => {
    fetchItems();
  }, [fetchItems]);

  const listItems = items.length
    ? items.map(item => {
        return <li key={item.id}>{item.title}</li>;
      })
    : "";

  return (
    <div>
      <ul>{listItems}</ul>
    </div>
  );
};

const App = () => {
  return (
    <Provider>
      <Filters />
    </Provider>
  );
};

const rootElement = document.getElementById("root");

ReactDOM.render(
    <App />,
  rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

无法让代码片段在 SO 上工作,这是一个工作沙箱:https : //codesandbox.io/s/falling-frog-4pdm1

谢谢!

1个回答

我想你可以使用自定义钩子围绕这个写一些抽象-

const identity = x => x

const useAsync = (runAsync = identity, deps = []) => {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)

  useEffect(_ => {
    Promise.resolve(runAsync(...deps))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  }, deps)

  return { loading, error, result }
}

你很兴奋所以你马上开始使用它 -

const MyComponent = () => {
  const { loading, error, result:items } =
    useAsync(_ => {
      axios.get("path/to/json")
        .then(res => res.json())
    }, ...)

  // ...
}

但停在那里。需要时编写更多有用的钩子 -

const fetchJson = (url) =>
  axios.get(url).then(r => r.json())

const useJson = (url) =>
  useAsync(fetchJson, [url])

const MyComponent = () => {
  const { loading, error, result:items } =
    useJson("path/to/json")

  if (loading)
    return <p>Loading...</p>

  if (error)
    return <p>Error: {error.message}</p>

  return <div><Items items={items} /></div>
}

方便地useEffect只会在依赖项更改时重新运行效果。但是,如果您希望使用更精细的控制来处理昂贵的查询,请查看useCallbackuseMemo