使用自定义 React 钩子获取数据
Fetch data with a custom React hook
我是 React 的新手,但我正在开发一个应用程序,当用户打开应用程序时,它会从服务器加载一些数据。 App.js 渲染此 AllEvents.js 组件:
const AllEvents = function ({ id, go, fetchedUser }) {
const [popout, setPopout] = useState(<ScreenSpinner className="preloader" size="large" />)
const [events, setEvents] = useState([])
const [searchQuery, setSearchQuery] = useState('')
const [pageNumber, setPageNumber] = useState(1)
useEvents(setEvents, setPopout) // get events on the main page
useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber)
// for ajax pagination
const handleSearch = (searchQuery) => {
setSearchQuery(searchQuery)
setPageNumber(1)
}
return(
<Panel id={id}>
<PanelHeader>Events around you</PanelHeader>
<FixedLayout vertical="top">
<Search onChange={handleSearch} />
</FixedLayout>
{popout}
{
<List id="event-list">
{
events.length > 0
?
events.map((event, i) => <EventListItem key={event.id} id={event.id} title={event.title} />)
:
<InfoMessages type="no-events" />
}
</List>
}
</Panel>
)
}
export default AllEvents
useEvents() 是 EventServerHooks.js 文件中的自定义挂钩。 EventServerHooks 是为封装不同的 ajax 请求而设计的。 (就像一个使 AllEvents.js 更干净的辅助文件)它是:
function useEvents(setEvents, setPopout) {
useEffect(() => {
axios.get("https://server.ru/events")
.then(
(response) => {
console.log(response)
console.log(new Date())
setEvents(response.data.data)
setPopout(null)
},
(error) => {
console.log('Error while getting events: ' + error)
}
)
}, [])
return null
}
function useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber) {
useEffect(() => {
setPopout(<ScreenSpinner className="preloader" size="large" />)
let cancel
axios({
method: 'GET',
url: "https://server.ru/events",
params: {q: searchQuery, page: pageNumber},
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(
(response) => {
setEvents(response.data)
setPopout(null)
},
(error) => {
console.log('Error while getting events: ' + error)
}
).catch(
e => {
if (axios.isCancel(e)) return
}
)
return () => cancel()
}, [searchQuery, pageNumber])
return null
}
export { useEvents, useSearchedEvents }
这里是第一个代码清单中的小组件 InfoMessages,如果事件数组为空,它会显示消息 "No results":
const InfoMessages = props => {
switch (props.type) {
case 'no-events':
{console.log(new Date())}
return <Div className="no-events">No results :(</Div>
default:
return ''
}
}
export default InfoMessages
所以我的问题是事件会定期加载,而在应用程序打开后不会定期加载。正如您在代码中看到的那样,我将控制台日志放在 useEvents() 和 InfoMessages 中,所以当它显示时它看起来像这样:
logs if events are displayed, and the app itself
如果没有显示,它看起来像这样:logs if events are not displayed, and the app itself
我必须指出,在这两种情况下,来自服务器的数据都已完美加载,所以我完全不知道为什么使用相同的代码会有不同的行为。我错过了什么?
不要将挂钩传递给自定义挂钩:自定义挂钩应该与特定组件分离并可能被重用。此外,您的自定义挂钩 return 总是 null
,这是错误的。但是您的代码很容易修复。
在您的主要组件中,您可以使用自定义挂钩获取数据,还可以像这样获取加载状态,例如:
function Events () {
const [events, loadingEvents] = useEvents([])
return loadingEvents ? <EventsSpinner /> : <div>{events.map(e => <Event key={e.id} title={e.title} />}</div>
}
在您的自定义挂钩中,您应该 return 内部状态。例如:
function useEvents(initialState) {
const [events, setEvents] = useState(initialState)
const [loading, setLoading] = useState(true)
useEffect(function() {
axios.get("https://server.ru/events")
.then(
(res) => {
setEvents(res.data)
setLoading(false)
}
)
}, [])
return [events, loading]
}
在此示例中,自定义挂钩 return 是一个数组,因为我们需要两个值,但您也可以 return 具有两个 key/value 对的对象。或者一个简单的变量(例如只有 events
数组,如果你不想要 loading
状态),然后像这样使用它:
const events = useEvents([])
这是您可以使用的另一个示例,它创建了一个执行获取信息任务的自定义挂钩
export const useFetch = (_url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(true);
useEffect(function() {
setLoading('procesando...');
setData(null);
setError(null);
const source = axios.CancelToken.source();
setTimeout( () => {
axios.get( _url,{cancelToken: source.token})
.then(
(res) => {
setLoading(false);
console.log(res.data);
//setData(res);
res.data && setData(res.data);
// res.content && setData(res.content);
})
.catch(err =>{
setLoading(false);
setError('si un error ocurre...');
})
},1000)
return ()=>{
source.cancel();
}
}, [_url])
我是 React 的新手,但我正在开发一个应用程序,当用户打开应用程序时,它会从服务器加载一些数据。 App.js 渲染此 AllEvents.js 组件:
const AllEvents = function ({ id, go, fetchedUser }) {
const [popout, setPopout] = useState(<ScreenSpinner className="preloader" size="large" />)
const [events, setEvents] = useState([])
const [searchQuery, setSearchQuery] = useState('')
const [pageNumber, setPageNumber] = useState(1)
useEvents(setEvents, setPopout) // get events on the main page
useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber)
// for ajax pagination
const handleSearch = (searchQuery) => {
setSearchQuery(searchQuery)
setPageNumber(1)
}
return(
<Panel id={id}>
<PanelHeader>Events around you</PanelHeader>
<FixedLayout vertical="top">
<Search onChange={handleSearch} />
</FixedLayout>
{popout}
{
<List id="event-list">
{
events.length > 0
?
events.map((event, i) => <EventListItem key={event.id} id={event.id} title={event.title} />)
:
<InfoMessages type="no-events" />
}
</List>
}
</Panel>
)
}
export default AllEvents
useEvents() 是 EventServerHooks.js 文件中的自定义挂钩。 EventServerHooks 是为封装不同的 ajax 请求而设计的。 (就像一个使 AllEvents.js 更干净的辅助文件)它是:
function useEvents(setEvents, setPopout) {
useEffect(() => {
axios.get("https://server.ru/events")
.then(
(response) => {
console.log(response)
console.log(new Date())
setEvents(response.data.data)
setPopout(null)
},
(error) => {
console.log('Error while getting events: ' + error)
}
)
}, [])
return null
}
function useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber) {
useEffect(() => {
setPopout(<ScreenSpinner className="preloader" size="large" />)
let cancel
axios({
method: 'GET',
url: "https://server.ru/events",
params: {q: searchQuery, page: pageNumber},
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(
(response) => {
setEvents(response.data)
setPopout(null)
},
(error) => {
console.log('Error while getting events: ' + error)
}
).catch(
e => {
if (axios.isCancel(e)) return
}
)
return () => cancel()
}, [searchQuery, pageNumber])
return null
}
export { useEvents, useSearchedEvents }
这里是第一个代码清单中的小组件 InfoMessages,如果事件数组为空,它会显示消息 "No results":
const InfoMessages = props => {
switch (props.type) {
case 'no-events':
{console.log(new Date())}
return <Div className="no-events">No results :(</Div>
default:
return ''
}
}
export default InfoMessages
所以我的问题是事件会定期加载,而在应用程序打开后不会定期加载。正如您在代码中看到的那样,我将控制台日志放在 useEvents() 和 InfoMessages 中,所以当它显示时它看起来像这样: logs if events are displayed, and the app itself
如果没有显示,它看起来像这样:logs if events are not displayed, and the app itself
我必须指出,在这两种情况下,来自服务器的数据都已完美加载,所以我完全不知道为什么使用相同的代码会有不同的行为。我错过了什么?
不要将挂钩传递给自定义挂钩:自定义挂钩应该与特定组件分离并可能被重用。此外,您的自定义挂钩 return 总是 null
,这是错误的。但是您的代码很容易修复。
在您的主要组件中,您可以使用自定义挂钩获取数据,还可以像这样获取加载状态,例如:
function Events () {
const [events, loadingEvents] = useEvents([])
return loadingEvents ? <EventsSpinner /> : <div>{events.map(e => <Event key={e.id} title={e.title} />}</div>
}
在您的自定义挂钩中,您应该 return 内部状态。例如:
function useEvents(initialState) {
const [events, setEvents] = useState(initialState)
const [loading, setLoading] = useState(true)
useEffect(function() {
axios.get("https://server.ru/events")
.then(
(res) => {
setEvents(res.data)
setLoading(false)
}
)
}, [])
return [events, loading]
}
在此示例中,自定义挂钩 return 是一个数组,因为我们需要两个值,但您也可以 return 具有两个 key/value 对的对象。或者一个简单的变量(例如只有 events
数组,如果你不想要 loading
状态),然后像这样使用它:
const events = useEvents([])
这是您可以使用的另一个示例,它创建了一个执行获取信息任务的自定义挂钩
export const useFetch = (_url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(true);
useEffect(function() {
setLoading('procesando...');
setData(null);
setError(null);
const source = axios.CancelToken.source();
setTimeout( () => {
axios.get( _url,{cancelToken: source.token})
.then(
(res) => {
setLoading(false);
console.log(res.data);
//setData(res);
res.data && setData(res.data);
// res.content && setData(res.content);
})
.catch(err =>{
setLoading(false);
setError('si un error ocurre...');
})
},1000)
return ()=>{
source.cancel();
}
}, [_url])