将 useContext/useReducer 与 useQuery 一起使用时,没有数据传递到状态
no data is passed into state when using useContext/useReducer together with useQuery
export const itemReducer = (state, action) => {
switch (action.type) {
default:
return state
}
}
import React, { useState, useReducer, createContext, useContext } from 'react'
import { useQuery } from '@apollo/client'
import { CURRENT_MONTH_BY_USER } from '../graphql/queries'
import { itemReducer } from '../reducers/ItemReducer'
const Items = createContext()
export const ItemProvider = ({ children }) => {
let items = []
const [state, dispatch] = useReducer(itemReducer, { items: items })
const result = useQuery(CURRENT_MONTH_BY_USER)
if (result.data && result.data.getCurrentMonthByUser) {
items = [...result.data.getCurrentMonthByUser]
}
return <Items.Provider value={{ state, dispatch }}>{children}</Items.Provider>
}
export const ItemsState = () => {
return useContext(Items)
}
export default ItemProvider
let items
从 useQuery 获取正确的数据,但是没有任何内容传递到状态,因此我无法将数据从上下文传输到另一个组件。我在这里做错了什么?
在调试 items
和 state
时,由于加载,它们最初是空的,但是只有 items
接收到正确的数据并且状态保持为空数组。
如果我将静态数据放入 let items
它工作得很好,所以也许我的 useQuery 也有问题?
如果您查看使用 items
的位置,就很容易看出您的问题。这只是您的 useReducer
调用的初始状态 - 但 items
仅设置为 non-empty 值 在 之后。这对组件绝对没有影响,因为 items
以后不会在您的组件函数中使用,并且初始状态只会在第一次渲染时设置一次。
要解决这个问题,您需要接受 reducer 的使用,添加一个新的操作类型来设置此初始数据,然后在您拥有数据时调度它。所以在你的减速器中添加这样的东西:
export const itemReducer = (state, action) => {
switch (action.type) {
case SET_INITIAL_DATA: // only a suggestion for the name, and obviously you need to define this as a constant
return { ...state, items: action.items };
/* other actions here */
default:
return state
}
}
然后像这样重写你的组件:
export const ItemProvider = ({ children }) => {
const [state, dispatch] = useReducer(itemReducer, { items: [] })
const result = useQuery(CURRENT_MONTH_BY_USER)
if (result.data && result.data.getCurrentMonthByUser) {
dispatch({ type: SET_INITIAL_DATA, items: result.data.getCurrentMonthByUser });
}
return <Items.Provider value={{ state, dispatch }}>{children}</Items.Provider>
}
此外,虽然这与您的问题无关,但我会注意到您的 ItemsState
导出似乎是一个自定义挂钩(它不能是其他任何东西,因为它不是组件而是使用hook) - 这完全没问题,但在 React 中有一个非常严格的约定,即所有自定义挂钩的名称都采用 useXXX
形式,我强烈建议您遵循该约定。因此,您可以将其重命名为 useItemsState
(我更喜欢 useItemsContext
以表明它只是一个专门针对您的特定上下文的 useContext
挂钩)。
export const itemReducer = (state, action) => {
switch (action.type) {
default:
return state
}
}
import React, { useState, useReducer, createContext, useContext } from 'react'
import { useQuery } from '@apollo/client'
import { CURRENT_MONTH_BY_USER } from '../graphql/queries'
import { itemReducer } from '../reducers/ItemReducer'
const Items = createContext()
export const ItemProvider = ({ children }) => {
let items = []
const [state, dispatch] = useReducer(itemReducer, { items: items })
const result = useQuery(CURRENT_MONTH_BY_USER)
if (result.data && result.data.getCurrentMonthByUser) {
items = [...result.data.getCurrentMonthByUser]
}
return <Items.Provider value={{ state, dispatch }}>{children}</Items.Provider>
}
export const ItemsState = () => {
return useContext(Items)
}
export default ItemProvider
let items
从 useQuery 获取正确的数据,但是没有任何内容传递到状态,因此我无法将数据从上下文传输到另一个组件。我在这里做错了什么?
在调试 items
和 state
时,由于加载,它们最初是空的,但是只有 items
接收到正确的数据并且状态保持为空数组。
如果我将静态数据放入 let items
它工作得很好,所以也许我的 useQuery 也有问题?
如果您查看使用 items
的位置,就很容易看出您的问题。这只是您的 useReducer
调用的初始状态 - 但 items
仅设置为 non-empty 值 在 之后。这对组件绝对没有影响,因为 items
以后不会在您的组件函数中使用,并且初始状态只会在第一次渲染时设置一次。
要解决这个问题,您需要接受 reducer 的使用,添加一个新的操作类型来设置此初始数据,然后在您拥有数据时调度它。所以在你的减速器中添加这样的东西:
export const itemReducer = (state, action) => {
switch (action.type) {
case SET_INITIAL_DATA: // only a suggestion for the name, and obviously you need to define this as a constant
return { ...state, items: action.items };
/* other actions here */
default:
return state
}
}
然后像这样重写你的组件:
export const ItemProvider = ({ children }) => {
const [state, dispatch] = useReducer(itemReducer, { items: [] })
const result = useQuery(CURRENT_MONTH_BY_USER)
if (result.data && result.data.getCurrentMonthByUser) {
dispatch({ type: SET_INITIAL_DATA, items: result.data.getCurrentMonthByUser });
}
return <Items.Provider value={{ state, dispatch }}>{children}</Items.Provider>
}
此外,虽然这与您的问题无关,但我会注意到您的 ItemsState
导出似乎是一个自定义挂钩(它不能是其他任何东西,因为它不是组件而是使用hook) - 这完全没问题,但在 React 中有一个非常严格的约定,即所有自定义挂钩的名称都采用 useXXX
形式,我强烈建议您遵循该约定。因此,您可以将其重命名为 useItemsState
(我更喜欢 useItemsContext
以表明它只是一个专门针对您的特定上下文的 useContext
挂钩)。