阿波罗查询未触发useEffect

时间:2019-12-12 20:55:43

标签: reactjs react-hooks react-apollo

我需要对Apollo查询进行重新读取以触发useEffect。我正在实现一个过滤器栏,该过滤器栏可以按日期段和文本搜索进行过滤。我的搜索“提交”按钮触发对查询的重新获取,并且返回的数据是我useEffect的依赖项。返回数据后,如果填充了搜索字段,则应运行过滤器。这在我第一次运行搜索时有效,但是当我使用重置功能重置时,重置被触发,并且查询在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

1 个答案:

答案 0 :(得分:1)

我意识到这是一个古老的问题,但是我遇到了类似的问题,来到这里是一个尚未回答的问题。我想与Apollo Query或其他任何用例分享我对useEffect挂钩的了解,以帮助其他人。

useEffect挂钩不会主动“监视”并且仅在选定的情况下触发。从graphql查询加载数据不是触发器之一。 useEffect将运行:

  • 重新渲染布局后,安装
  • 如果组件的状态发生更改
  • 如果组件的道具发生更改
  • 卸载
  • 组件时

请查看此amazing article以获得更多详细信息。

此外,如果将变量放置在useEffect钩子末尾的依存关系数组中,则按设计 进行,如果变量没有更改,它将不会运行。

因此,为了在从Apollo查询加载数据后触发useEffect,您必须使数据加载时发生其中一种情况。

就我而言,在加载后简单地使用数据要容易得多,因为尝试将异步的setState与异步的数据加载同步会产生一些变化且不可预测的结果。

使用父类并传入变量作为道具确实有帮助,该变量在更改时会触发useEffect到setState。该变量用于实际的Apollo查询中,因此对整体设计有意义。

这里是一个例子:

    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>

我希望这会有所帮助并回答问题。