我需要重新获取Apollo查询以触发useEffect。我正在实现一个按日期周期和文本搜索过滤的过滤器栏。我的搜索提交按钮触发了查询的重新获取,返回的数据是我的 useImpact 的依赖项。返回数据时,如果填充了搜索字段,则应运行筛选器。这在我第一次运行搜索时有效,但是当我使用重置功能重置时,重置被触发,查询在 Chrome 中得到 200 并在 Chrome 开发工具中显示数据,但 useEffect 未触发。这是为什么呢?
//React
import React, {useState, useContext, useEffect, Fragment } from 'react'
//extra React libraries
//external libraries
import { useQuery, useMutation } from '@apollo/react-hooks'
//components
import AccountsGrid from '../../containers/AccountsGrid/AccountsGrid'
import SpinnerLoader from '../../components/SpinnerLoader/SpinnerLoader'
//styles
import styles from "./accounts.module.scss";
//contexts
import UserContext from '../../containers/contexts/UserContext/UserContext'
import AccountContext from '../../containers/contexts/AccountContext/AccountContext'
//constants
import {
GET_PARENT_ACCOUNT,
SET_ACCOUNTS,
SET_IS_ACTIVE,
SET_ACCOUNTS_LIMIT,
SET_ACCOUNTS_OFFSET,
SET_ACCOUNTS_TOTAL,
SET_ACCOUNTS_START_DATE,
SET_ACCOUNTS_END_DATE,
} from '../../containers/contexts/AccountContext/accountActionTypes'
import {
GET_ACCOUNT_USERS,
GET_ACCOUNTS,
TOTAL_ACCOUNTS,
} from '../../utils/constants/queries/accountQueries'
import {
UPDATE_ACCOUNT
} from '../../utils/constants/mutations/accountMutations'
import moment from 'moment';
function Accounts(props) {
const userContext = useContext(UserContext)
const accountContext = useContext(AccountContext)
const parentAccountID = userContext.userState.accountId
const [parentAccountIDs, setParentAccountIDs] = useState(null)
const [sortByField, setSortByField] = React.useState(null);
const [sortByType, setSortByType] = React.useState(null);
const [startDate, setStartDate] = React.useState(null);
const [endDate, setEndDate] = React.useState(null);
const [datesValid, setDatesValid] = React.useState(true);
const [searchText, setSearchText] = React.useState("");
const {
loading: loadingAccountUsers,
error: errorAccountUsers,
data: dataAccountUsers,
refetch: refetchAccountUsers,
} = useQuery(GET_ACCOUNT_USERS, {
variables: {
accountId: parentAccountID
}
})
const {
loading: loadingAccounts,
error: errorAccounts,
data: dataAccounts,
refetch: refetchAccounts
} = useQuery(GET_ACCOUNTS, {
variables: {
parentIds: parentAccountIDs,
offset: accountContext.accountState.data.accountsOffset,
limit: accountContext.accountState.data.accountsLimit
}
})
const {
loading: loadingAccountsTotal,
error: errorAccountsTotal,
data: dataAccountsTotal,
refetch: refetchAccountsTotal
} = useQuery(TOTAL_ACCOUNTS)
const [
updateAccount,
{ loading, error, data: updateAccountData }
] = useMutation(UPDATE_ACCOUNT);
const setParentIDsHandler = (id) => {
setParentAccountIDs(String(id))
}
const setOffset = (offset, limit) => {
accountContext.accountDispatch({
type: SET_ACCOUNTS_OFFSET,
payload: {
offset: offset
}
})
accountContext.accountDispatch({
type: SET_ACCOUNTS_LIMIT,
payload: {
limit: limit
}
})
}
const deactivateUser = row => {
updateAccount({
variables: {
account: {
id: row.id,
isActive: !row.isActive
}
}
})
accountContext.accountDispatch({type: SET_IS_ACTIVE, payload: row.id})
}
const handleRequestSort = (sortType, sortBy) => {
sortRow(sortBy)
setSortByType(sortType)
}
const sortRow = (sortBy) => {
switch(sortBy) {
case 'Contact':
setSortByField('email')
break
case 'First Name':
setSortByField('firstName')
break
case 'Last Name':
setSortByField('lastName')
break
case 'Join Date':
setSortByField('dateJoined')
break
}
}
const setDates = (dates) => {
console.log("SETTING DATES", dates)
if ("startDate" === Object.keys(dates)[0]) {
setStartDate(dates.startDate)
} else {
setEndDate(dates.endDate)
}
}
const checkDatesValid = () => {
if (startDate && endDate) {
if (startDate <= endDate) {
return setDatesValid(true)
}
}
return setDatesValid(false)
}
const clearDates = () => {
console.log("CLEARING")
setDatesValid(true)
setStartDate(null)
setEndDate(null)
setSearchText(null)
accountContext.accountDispatch({type: SET_ACCOUNTS_START_DATE, payload: {startDate: null}})
accountContext.accountDispatch({type: SET_ACCOUNTS_END_DATE, payload: {endDate: null}})
return setDatesValid(true)
}
const searchByChars = (text) => {
console.log("SEARCH TEXT", text)
setSearchText(text)
resetAccounts()
}
const resetAccounts = () => {
console.log("RESET ACCOUNTS TRIGGERED")
refetchAccounts({
variables: {
parentIds: parentAccountIDs,
offset: accountContext.accountState.data.accountsOffset,
limit: accountContext.accountState.data.accountsLimit
}
})
console.log("LOADING", loadingAccounts)
console.log("ERROR", errorAccounts)
}
const filterText = (textRows) => {
let newTextRows = []
for (let row of textRows) {
Object.entries(row['users'][0]).forEach(([key, val]) => {
if (String(val).includes(String(searchText))) {
if (!(newTextRows.includes(row))) {
newTextRows.push(row)
}
}
});
}
accountContext.accountDispatch({type: SET_ACCOUNTS, payload: {accounts: newTextRows}})
}
const filterDates = (dateRows) => {
let newDateRows = []
for (let row of dateRows) {
if (datesValid) {
const _date = moment(row.users[0]['dateJoined'])
const sdate = moment(startDate)
const edate = moment(endDate)
if (_date.isBetween(sdate, edate, null, '[]')) {
if (!(newDateRows.includes(row))) {
newDateRows.push(row)
}
}
}
}
accountContext.accountDispatch({type: SET_ACCOUNTS, payload: {accounts: newDateRows}})
}
useEffect(() => {
if (sortByField && sortByType) {
const newRows = accountContext.accountState.data.accounts.sort((a, b) => {
const compareA = a.users[0][sortByField]
const compareB = b.users[0][sortByField]
if (compareA || compareB) {
if ("DESC" === sortByType) {
let comparison = 0;
if (compareA > compareB) {
comparison = 1;
} else if (compareA < compareB) {
comparison = -1;
}
return comparison;
} else {
let comparison = 0;
if (compareA < compareB) {
comparison = 1;
} else if (compareA > compareB) {
comparison = -1;
}
return comparison;
}
}
})
accountContext.accountDispatch({type: SET_ACCOUNTS, payload: newRows})
}
if (dataAccountsTotal) {
accountContext.accountDispatch({type: SET_ACCOUNTS_TOTAL, payload: dataAccountsTotal})
}
if (dataAccountUsers) {
setParentIDsHandler(dataAccountUsers.accountUsers[0].account.id)
accountContext.accountDispatch({type: GET_PARENT_ACCOUNT, payload: dataAccountUsers})
}
if (dataAccounts) {
console.log("NEW DATA", searchText, startDate, endDate, datesValid)
console.log("SEARCH TEXT", searchText)
console.log("START DATE", startDate)
console.log("END DATE", endDate)
accountContext.accountDispatch({type: SET_ACCOUNTS, payload: dataAccounts})
console.log("DATES VALID", datesValid)
if (startDate
&& endDate && datesValid) {
console.log("FILTER BY DATES")
filterDates(accountContext.accountState.data.accounts)
}
if (searchText) {
filterText(dataAccounts)
}
}
}, [
loadingAccounts,
dataAccounts,
dataAccountsTotal,
dataAccountUsers,
parentAccountIDs,
updateAccountData,
sortByField,
sortByType,
searchText
])
return (
<Fragment>
{
accountContext.accountState.data.accounts &&
!loadingAccountUsers &&
!errorAccountUsers &&
!loadingAccounts &&
!errorAccounts &&
parentAccountIDs &&
accountContext.accountState.data.accountUsers
? <AccountsGrid
setOffset={setOffset}
currentStartDate={startDate}
currentEndDate={endDate}
searchText={searchByChars}
datesValid={datesValid}
resetAccounts={resetAccounts}
setDates={setDates}
clearDates={clearDates}
deactivateUser={deactivateUser}
handleRequestSort={handleRequestSort}
accountRows={accountContext.accountState.data.accounts}
/> : <SpinnerLoader />}
</Fragment>
)
}
export default Accounts
我意识到这是一个老问题,但我遇到了类似的问题,并来到这里提出了一个未解决的问题。 我想分享我所学到的关于 useEffect 钩子与 Apollo Query 或任何其他用例,如果它对其他人有帮助的话。
useEffect 钩子不会主动"监视",只会在特定情况下触发。从 graphql 查询加载数据不是触发器之一。 useEffect将运行:
- 在 组件装载时重新渲染布局后
- 如果组件的状态发生更改
- 如果组件的道具发生变化
- 当组件卸载时
请查看这篇精彩的文章以获取更多详细信息。
此外,如果变量放置在 useEffect 钩子末尾的依赖数组中,根据设计,如果变量没有更改,它将不会运行。
因此,为了在从 Apollo 查询加载数据后触发 useEffect,您必须在数据加载时使这些条件之一发生。
在我的情况下,加载后简单地使用数据要容易得多,因为尝试将异步的setState与异步的数据加载同步会产生一些不同且不可预测的结果。
它确实有助于使用父类并传入一个变量作为道具,当它发生变化时,它会触发 useEffect 到 setState。 该变量用于实际的阿波罗查询,因此对整体设计有意义。
下面是一个示例:
export const ResultsGrid = ( queryTerm ) => {
const [searchTerm, setSearchTerm] = useState('')
const { data, error, loading } = useQuery(GET_RESULTS_QUERY, {variables: { searchTerm }, })
useEffect(() => {setSearchTerm(queryTerm)}
,[queryTerm]) //useEffect will run only when queryTerm changes
if (loading) return <p>Loading data from query...</p>
if (error)
return (
<React.Fragment>
<p>Sorry, unable to fetch results. </p>
<button onClick={() => refetch}>Please try again!</button>
{console.log("error", error)}
</React.Fragment>
)
console.log('data:', data)
console.log('loading:', loading)
const { results } = data
return (
//display data results here
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.1/umd/react-dom.production.min.js"></script>
我希望这是有帮助的,并回答了这个问题。