Please help me understand why am I getting no items on first request. I have this component ExportData that calls Context only when prompted, dialogConfirmation is linked to a button, snippet:
ExportData:
import useItem from '../../context/ItemsContext'
const ExportData = () => {
const { items, getAll } = useItem()
const dialogConfirmation = confirm => {
if (confirm) {
getAll()
}
}
}
ItemsContext:
import React, {
useState,
useReducer,
createContext,
useContext,
useEffect,
} from 'react'
import { useQuery } from '@apollo/client'
import { CURRENT_MONTH, ALL_ITEMS } from '../graphql/queries'
import itemReducer from '../reducers/itemReducer'
export const ItemProvider = ({ children }) => {
let items = []
const [all, setAll] = useState(false)
const selectedMonth = dayjs(new Date()).format('YYYY-MM')
const [state, dispatch] = useReducer(itemReducer, [])
const result = useQuery(all ? ALL_ITEMS : CURRENT_MONTH, {
variables: all ? null : { selectedMonth },
})
if (result.data && result.data.getCurrentMonth) {
items = [...result.data.getCurrentMonth]
}
if (result.data && result.data.getItems) {
items = [...result.data.getItems]
}
useEffect(() => {
if (result.data && result.data.getCurrentMonth) {
dispatch({
type: 'ALL',
items: result.data.getCurrentMonth,
})
}
if (result.data && result.data.getItems) {
dispatch({
type: 'ALL',
items: result.data.getItems,
})
}
}, [result.data])
const getAll = () => {
setAll(true)
}
const value = {
items: state.items,
getAll,
}
return <ItemContext.Provider value={value}>{children}</ItemContext.Provider>
}
Reducer:
const itemReducer = (state, action) => {
switch (action.type) {
case 'ALL':
return {
...state,
items: action.items,
}
default:
return state
}
}
Query CURRENT_MONTH returns only items from current month - that should be the default one.
Query ALL_ITEMS returns all items in database.
What happens currently, when I use the getAll function in ExportData, on the first try, it returns only the items from current month, however on the second try, it returns correct output(all data).
When debugging itemReducer, all items are in the action.items however the output within the component gets only the old data...On the second try however, it works fine.
What am I doing wrong here?
What is happening here:
useEffects are fired: (inside component) does nothing, (inside useQuery) fires request to gql apiuseQuery is set with datastate.items becouse dispatch was not fired yet)useEffects are fired and this time dispatch is called.In this process react renders 3 times. If you place console.log(state.items) inside component it will look like this:
[] // no data yet
[] // data is present but not yet used by `state`
[<your data>] // data is present and used by state
You can solve that by removing useEffect and useReducer:
useEffects are fired: (inside component) does nothing, (inside useQuery) fires request to gql apiuseQuery is set with dataIn this process react renders 2 times. If you place console.log(items) inside component it will look like this:
[] // no data yet
[<your data>] // data is present and used
import React, {
useState,
createContext,
useContext,
} from 'react'
import { useQuery } from '@apollo/client'
import { CURRENT_MONTH, ALL_ITEMS } from '../graphql/queries'
export const ItemProvider = ({ children }) => {
const [all, setAll] = useState(false)
const selectedMonth = dayjs(new Date()).format('YYYY-MM')
const result = useQuery(all ? ALL_ITEMS : CURRENT_MONTH, {
variables: all ? null : { selectedMonth },
})
const getAll = () => {
setAll(true)
}
const items = result?.data.getCurrentMonth || result?.data.getItems || []
const value = {
items,
getAll,
}
return <ItemContext.Provider value={value}>{children}</ItemContext.Provider>
}