StateProvider.js
import React, { createContext, useContext, useReducer } from "react";
// Prepares the dataLayer
export const StateContext = createContext();
// Wrap our app and provide the Data layer
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
// Pull information from the data layer
export const useStateValue = () => useContext(StateContext);
Reducer.js
export const initialState = {
basket: [],
};
const reducer = (state, action) => {
console.log(action);
switch (action.type) {
case "ADD_TO_BASKET":
return {
...state,
basket: [**your text**...state.basket, action.item],
};
}
}
export default reducer;
Proudct.js
import React from 'react'
import '../css/Proudct.css'
import { useStateValue } from './StateProvider';
import { nanoid } from 'nanoid'
function Proudct({ title, currency, price, rate, image }) {
const [{ basket }, dispatch] = useStateValue();
const addToBasket = () => {
// dispatch the item into the data layer
dispatch({
type: "ADD_TO_BASKET",
item: {
title: title,
image: image,
price: price,
rate: rate,
},
});
};
return (
<div className='proudct'>
<p className='proudct--title'>{title}</p>
<div className='proudct--price'>
<small>{currency}</small>
<strong>{price}</strong>
</div>
<span className='proudct--rate'>{rate}</span>
<div className='proudct--image'>
<img src={image} />
</div>
<button onClick={addToBasket} className='proudct--button'>Add to Basket</button>
</div>
)
}
export default Proudct;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { createBrowserRouter, RouterProvider } from "react-router-dom"
import Checkout from './compenets/Checkout';
import Home from './compenets/Home';
import reducer, { initialState } from './compenets/Reducer';
import { StateProvider } from './compenets/StateProvider';
const routes = createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{
index: true , element: <Home />
},
{
path: "checkout",
element: <Checkout />
}
]
}
])
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<RouterProvider router ={routes} />
<StateProvider initalState={initialState} reducer={reducer}>
</StateProvider>
</React.StrictMode>
)
reportWebVitals();
未处理的抛出错误!undefined不可迭代(无法读取属性Symbol(Symbol.iterator((TypeError:undefined不可迭代(无法读取属性Symbol(Symbol.iterator((在Proudct
当你的应用程序抛出错误时,你可以在<Route>
上提供自己的errorElement道具,从而提供比这更好的用户体验
问题
您似乎没有为应用程序提供上下文值。
root.render(
<React.StrictMode>
<RouterProvider router={routes} />
<StateProvider initialState={initialState} reducer={reducer}>
</StateProvider>
</React.StrictMode>
);
没有默认的上下文值:
export const StateContext = createContext(); // <-- default value is undefined
当消费者试图破坏上下文值时,他们不能,因为他们接收的上下文值是未定义的。没有什么可以迭代和破坏的。
const [{ basket }, dispatch] = useStateValue(); // <-- can't iterate undefined
解决方案
提供默认上下文值是常见的,但不是必须的:
export const StateContext = createContext([
{ basket: [] }, // <-- object with expected properties and empty/default vlaues
() => {} // <-- "dispatch" function
]);
上下文提供者仍然需要包装和呈现它为其提供值的ReactTree
StateProvider
组件应该包装RouterProvider
组件,以便在ReactTree中呈现的路由在其上方有一个上下文提供程序。
示例:
root.render(
<React.StrictMode>
<StateProvider initialState={initialState} reducer={reducer}>
<RouterProvider router={routes} />
</StateProvider>
</React.StrictMode>
);