我很难弄清楚从 API 获取数据一次(以及第一次请求时(然后存储结果以供重用和/或其他组件函数的最佳方法。下面是一个具有useContext
和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
谢谢!
我想你可以使用自定义钩子围绕它写一些抽象 -
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>
}
方便使用效果只会在依赖项更改时重新运行效果。但是,如果您希望使用更精细的控制来处理昂贵的查询,请查看 useCallback 和 useMemo。