使用 UseEffect 调用端点
Using UseEffect to Call an Endpoint
我正在使用 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>
<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;
我有几个问题:
- 假设我不想在加载组件时调用查询(并且不想在加载时发出 GET 请求)。怎么做到的?
- 想象一下,按钮 1 和 2 触发的事件实际上是对不同 API 端点的调用。例如,按钮 1 可以代表一个调用,以查明刚刚输入的特定用户名是否已经存在。按钮 2 可以代表一个调用,以查明刚刚输入的电子邮件地址是否已经存在。在这种情况下,确保正确使用由
doFetch
填充的新 data
的常见做法是什么?
如果问题 2 不清楚,让我换一种说法:在所有情况下,data
对象由 useDataApi
填充。但是,如果第一次调用 useDataApi
是为了填充一个东西,第二次调用是为了填充其他东西,第三次调用是为了再次填充其他东西,那么处理这种逻辑的最佳方法是什么?
Suppose I don't want to call the query upon component load (and don't want a GET request going out upon load). How would one do that?
您可以将一个空字符串传递给您的挂钩并创建一个条件,即当 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];
}
Imagine that the events triggered by Buttons 1 & 2 were actually each calls to different API Endpoints. For example, Button 1 could represent a call to find out if a particular username just entered already existed. Button 2 could represent a call to find out if an e-mail address just entered already existed. In such cases, what is the common practice to ensure that the new data populated by doFetch is used properly?
由你决定。在我看来,您已经完成了处理新数据的高级部分:使用减速器。我会考虑将 data
填充为一个对象,其中每个 属性 都是每个 API 响应的 ID。我的意思是,如果所有 APIs return 一个字段 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,
};
我正在使用 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>
<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;
我有几个问题:
- 假设我不想在加载组件时调用查询(并且不想在加载时发出 GET 请求)。怎么做到的?
- 想象一下,按钮 1 和 2 触发的事件实际上是对不同 API 端点的调用。例如,按钮 1 可以代表一个调用,以查明刚刚输入的特定用户名是否已经存在。按钮 2 可以代表一个调用,以查明刚刚输入的电子邮件地址是否已经存在。在这种情况下,确保正确使用由
doFetch
填充的新data
的常见做法是什么?
如果问题 2 不清楚,让我换一种说法:在所有情况下,data
对象由 useDataApi
填充。但是,如果第一次调用 useDataApi
是为了填充一个东西,第二次调用是为了填充其他东西,第三次调用是为了再次填充其他东西,那么处理这种逻辑的最佳方法是什么?
Suppose I don't want to call the query upon component load (and don't want a GET request going out upon load). How would one do that?
您可以将一个空字符串传递给您的挂钩并创建一个条件,即当 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];
}
Imagine that the events triggered by Buttons 1 & 2 were actually each calls to different API Endpoints. For example, Button 1 could represent a call to find out if a particular username just entered already existed. Button 2 could represent a call to find out if an e-mail address just entered already existed. In such cases, what is the common practice to ensure that the new data populated by doFetch is used properly?
由你决定。在我看来,您已经完成了处理新数据的高级部分:使用减速器。我会考虑将 data
填充为一个对象,其中每个 属性 都是每个 API 响应的 ID。我的意思是,如果所有 APIs return 一个字段 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,
};