简体   繁体   English

如何在 Next.js 中加载页面之前等待 React 上下文加载

[英]How to wait for React context to load before loading page in Next.js

I'm new to React and this is my first time using the Context API.我是 React 新手,这是我第一次使用 Context API。

I'm creating a site with Next.js which would list items added by users similar to an e-commerce website.我正在创建一个带有 Next.js 的网站,它将列出用户添加的项目,类似于电子商务网站。

I'm currently using useState and Context API to simulate a default list of items that would otherwise be pulled in from a database.我目前正在使用 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
    }
}

The context is provided globally within _app.js上下文在 _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

Then I use this context in my nested layout so that any pages with /browse can access the list of items, search through them, and filter them so that a list of filtered items can be rendered within the child component.然后我在我的嵌套布局中使用此上下文,以便任何带有 /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

Currently the site works as expected when starting from the index page and proceeding to the browse page.目前,该站点从索引页面开始并继续到浏览页面时按预期工作。 The item list gets populated and items can be added successfully.项目列表得到填充,项目可以成功添加。 However, if I attempt to enter the site through any page actively using the context (eg: http://localhost:3000/browse), I'm met with this error:但是,如果我尝试通过任何积极使用上下文的页面(例如:http://localhost:3000/browse)进入该站点,我会遇到此错误:

TypeError: Cannot destructure property 'items' of '(0, ItemContext__WEBPACK_IMPORTED_MODULE_1 _.useItems)(...)' as it is undefined.类型错误:无法解构“(0, ItemContext__WEBPACK_IMPORTED_MODULE_1 _.useItems)(...)”的属性“项目”,因为它未定义。

I'm assuming it is because the page loads before the Context does while starting from the index gives the Context time to load before it is accessed.我假设这是因为页面在 Context 之前加载,而从索引开始给 Context 时间在它被访问之前加载。 How would I go about addressing this error?我 go 如何解决这个错误? Is my usage of the Context API fundamentally flawed?我对上下文 API 的使用存在根本性缺陷吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM