【发布时间】:2021-10-12 10:20:29
【问题描述】:
我是 React 新手,这是我第一次使用 Context API。
我正在使用 Next.js 创建一个网站,该网站将列出用户添加的项目,类似于电子商务网站。
我目前正在使用 useState 和 Context API 来模拟默认的项目列表,否则这些项目会从数据库中提取出来。
import { createContext, useContext, useState } from 'react';
const ItemContext = createContext();
export function useItems() {
return useContext(ItemContext);
}
export function ItemContextProvider ({ children }) {
const itemContext = useItemContext();
return (
<ItemContext.Provider value={itemContext}>
{children}
</ItemContext.Provider>
)
}
function useItemContext () {
const [items, setItems] = useState([
{id: 1, name: 'Apple Earphones', img:'CLOUDINARY_IMAGE_LINK', location: 'location',
type: 'Electronics', contact: { Phone: '1234567890'},
used: 'Used More Than One Year', broken: 'Unbroken', description: "These are apple earphones with a lightning connector. Well used. Don't need them as I don't use an iPhone anymore."},
{id: 2, name: 'Xbox Controller', img:'CLOUDINARY_IMAGE_LINK', location: 'location',
type: 'Electronics', contact: { Phone: '1234567890' },
used: 'Used More Than One Year', broken: 'Unbroken', description: "These are apple earphones with a lightning connector. Well used. Don't need them as I don't use an iPhone anymore."},
{id: 3, name: 'Standing Fan', img:'CLOUDINARY_IMAGE_LINK', location: 'location',
type: 'Electronics', contact: { Phone: '1234567890' },
used: 'Used More Than One Year', broken: 'Unbroken', description: "These are apple earphones with a lightning connector. Well used. Don't need them as I don't use an iPhone anymore."},
{id: 4, name: 'Apple Earphones', img:'CLOUDINARY_IMAGE_LINK', location: 'location',
type: 'Electronics', contact: { Phone: '1234567890' },
used: 'Used More Than One Year', broken: 'Unbroken', description: "These are apple earphones with a lightning connector. Well used. Don't need them as I don't use an iPhone anymore."},
{id: 5, name: 'Xbox Controller', img:'CLOUDINARY_IMAGE_LINK', location: 'location',
type: 'Electronics', contact: { Phone: '1234567890' },
used: 'Used More Than One Year', broken: 'Unbroken', description: "These are apple earphones with a lightning connector. Well used. Don't need them as I don't use an iPhone anymore."},
])
function getSingleItem (id) {
return items.find(obj => obj.id == id)
}
function addItem (itemObject) {
setItems([...items, itemObject]);
}
return {
items,
getSingleItem,
addItem
}
}
上下文在 _app.js 中全局提供
import Layout from '../components/Layout'
import { ItemContextProvider } from '../components/ItemContext'
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
const getLayout = Component.getLayout || ((page) => page)
return (
<ItemContextProvider>
{getLayout(<Component {...pageProps} />)}
</ItemContextProvider>
)
}
export default MyApp
然后我在嵌套布局中使用此上下文,以便任何使用 /browse 的页面都可以访问项目列表、搜索它们并过滤它们,以便可以在子组件中呈现过滤后的项目列表。
import { createContext, useState, useEffect, useReducer } from 'react'
import { useItems } from './ItemContext'
import Navbar from './Navbar'
import Sidebar from './Sidebar'
import browseStyle from '../styles/Browse.module.css'
export const FilterContext = createContext();
const NestedLayout = ({ children }) => {
const { items } = useItems();
const [currentItems, setCurrentItems] = useState(items)
const [searchTerm, setSearchTerm] = useState('')
const [filters, setFilters] = useState(
{used: { Unused: true, UsedLessThanOneMonth: true, UsedFewMonths: true, UsedMoreThanOneYear: true },
broken: { Unbroken: true, PartiallyBroken: true, CompletelyBroken: true },
type: { Electronics: true, Books: true, CDs: true, Household: true, Furniture: true, Other: true },
});
const [filteredItems, setFilteredItems] = useState(currentItems);
function reducer (searchFilter, action) {
switch (action.type) {
case 'search':
if (action.payload.searchTerm === ('')) {
// setSearchTerm('')
searchFilter = currentItems;
} else {
// setSearchTerm(action.payload.searchTerm.toLowerCase());
searchFilter = currentItems.filter(item => item.name.toLowerCase().includes(action.payload.searchTerm.toLowerCase()))
}
return searchFilter;
default:
searchFilter = currentItems;
return searchFilter
}
}
const [searchFilter, dispatch] = useReducer(reducer, currentItems);
function filterReducer (filterFilter, action) {
switch (action.type) {
case 'filter':
filterFilter = currentItems.filter(item => getFilteredItems(item))
return filterFilter;
default:
filterFilter = currentItems;
return filterFilter;
}
}
const [filterFilter, dispatchFilter] = useReducer(filterReducer, currentItems);
useEffect (() => {
dispatchFilter({type: 'filter', payload: {filters: filters}})
}, [filters])
useEffect (() => {
setFilteredItems( searchFilter.filter(element => filterFilter.includes(element)))
},[searchFilter, filterFilter])
useEffect (()=> {
setCurrentItems(items)
dispatchFilter({type: 'filter', payload: {filters: filters}})
dispatch({ type: 'search', payload: { searchTerm: ''} })
}, [items])
function getFilteredItems(item) {
const noWhiteSpaces = item.used.replace(/\s+/g, '');
const itemProperties = [item.type, noWhiteSpaces, item.broken]
const allKeys = Object.entries(filters).map(category => Object.keys(category[1]))
const allKeysFlat = allKeys.flat();
const allEntries = Object.entries(filters).map(category => Object.entries(category[1]))
const allEntriesFlat = allEntries.flat()
const finalObject = Object.fromEntries(allEntriesFlat)
const filtered = allKeysFlat.filter(key => finalObject[key]);
const found = itemProperties.every(r=> filtered.includes(r))
return found;
}
return (
<FilterContext.Provider value={filteredItems}>
<main className={browseStyle.main}>
<Navbar dispatch={dispatch}/>
<div className={browseStyle.browse}>
<Sidebar filters={filters} setFilters={setFilters}/>
{children}
</div>
</main>
</FilterContext.Provider>
)
}
export default NestedLayout
目前,当从索引页面开始并进入浏览页面时,该站点按预期工作。项目列表被填充并且项目可以成功添加。但是,如果我尝试通过任何页面主动使用上下文(例如:http://localhost:3000/browse)进入该站点,我会遇到此错误:
TypeError:无法解构 '(0 , ItemContext__WEBPACK_IMPORTED_MODULE_1_.useItems)(...)' 的属性 'items',因为它是未定义的。
我假设这是因为页面在上下文之前加载,而从索引开始时,上下文在被访问之前有时间加载。我将如何解决这个错误?我对 Context API 的使用是否存在根本缺陷?
【问题讨论】:
-
检查这部分
const { items } = useItems(); -
在初始化提供程序时尝试解构
itemContext值:<ItemContext.Provider value={{...itemContext}}> -
@LucaPizzini 不幸的是我仍然遇到同样的错误
-
@ABDULLOKHMUKHAMMADJONOV 我知道那条线是造成错误的原因,但我不知道如何解决它。任何指针将不胜感激!
-
不是命名导出,所以
const items = useItems();
标签: javascript reactjs next.js frontend react-context