Задать вопрос
serii81
@serii81
Я люблю phр...

Как в react использовать Context в layout?

Всем привет.
Создал контекст в файле Context.js
import { createContext, useReducer } from 'react';
import { reducer } from './reducer';
import {
	CLOSE_ALERT,
	REMOVE_FROM_BASKET,
	SET_GOODS,
	SET_IS_BASKET_SHOW,
	SET_LOADING,
	UPDATE_CART,
} from './../reducer_vars/shop_vars';

export const ShopContext = createContext();
const initialState = {
	goods: [],
	loading: false,
	orders: [],
	isBasketShow: false,
	error: null,
	toast: '',
};
export const ContextProvider = ({ children }) => {
	const [value, dispatch] = useReducer(reducer, initialState);
	value.closeAlert = () => {
		dispatch({ type: CLOSE_ALERT });
	};
	value.removeBasketItem = (id) => {
		dispatch({ type: REMOVE_FROM_BASKET, payload: id });
	};
	value.updateCart = (item) => {
		dispatch({ type: UPDATE_CART, payload: item });
	};
	value.setGoods = (items) => {
		dispatch({ type: SET_GOODS, payload: items });
	};
	value.setLoading = (data) => {
		dispatch({ type: SET_LOADING, payload: data });
	};
	value.setIsBasketShow = (data) => {
		dispatch({ type: SET_IS_BASKET_SHOW, payload: data });
	};
	return (
		<ShopContext.Provider value={value}>{children}</ShopContext.Provider>
	);
};


В файле root.jsx находится точка входа в приложении
import { NavLink, Outlet } from 'react-router-dom';
// import { ContextProvider } from '../context/Context';

export default function Root() {
	return (
		<>
			<nav className="header">
				<div className="nav-wrapper">
					<ul className="left hide-on-med-and-down menu">
						<li>
							<NavLink to={'/'}>Home</NavLink>
						</li>
						<li>
							<NavLink to={'/hooks'}>Hooks</NavLink>
						</li>
						<li>
							<NavLink to={'/counter'}>Counter</NavLink>
						</li>
						<li>
							<NavLink to={'/reducer'}>Use Reducer</NavLink>
						</li>
						<li>
							<NavLink to={'/goods'}>Goods</NavLink>
						</li>
						<li>
							<NavLink to={'/todos'}>Todos</NavLink>
						</li>
					</ul>
				</div>
			</nav>
			<div className="main">
					<Outlet />
			</div>
			<footer className="page-footer">
				<div className="footer-copyright">
					<div className="container">
						© 2022 Copyright Text
						<a className="grey-text text-lighten-4 right" href="#!">
							More Links
						</a>
					</div>
				</div>
			</footer>
		</>
	);
}


Также сдоздал layout GoodsLayout.jsx
import React from 'react';
import { ContextProvider } from '../context/Context';

export default function GoodsLayout({ children }) {
	return (
		<ContextProvider>
			<div>{children}</div>
		</ContextProvider>
	);
}


Ну и страница GoodsPage.jsx
import { useContext, useEffect, useState } from 'react';
import GoodsLayout from '../layouts/GoodsLayout';
import Basket from '../components/goods/Basket';
import Cart from '../components/goods/Cart';
import GoodsList from '../components/goods/GoodsList';
import Preloader from '../components/UI/Preloader';
import Toast from '../components/UI/Toast';
import { APP_KEY, APP_URL } from '../config';
import { ShopContext } from '../context/Context';

const GoodsPage = () => {
	const {
		toast,
		goods,
		setGoods,
		loading,
		setLoading,
		setIsBasketShow,
		isBasketShow,
	} = useContext(ShopContext);
	const [error, setError] = useState(null);
	function toggleBasket() {
		setIsBasketShow(!isBasketShow);
	}
	useEffect(function getGoods() {
		setLoading(true);
		fetch(APP_URL, {
			headers: {
				Authorization: APP_KEY,
			},
		})
			.then((response) => response.json())
			.then((data) => {
				setGoods(data.shop);
				setTimeout(() => {
					setLoading(false);
				}, 1000);
			})
			.catch((error) => {
				setError('Access token is invalid');
				console.log(error, 'error');
				setLoading(false);
			});
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	return (
		<GoodsLayout>
			<div className="mt4">
				<div className="container">
					<h2>Goods</h2>
					{loading ? (
						<Preloader />
					) : error ? (
						<h3 className="center red-text">{error}</h3>
					) : (
						<GoodsList goods={goods} />
					)}
					<Cart toggleBasket={toggleBasket} />
				</div>
				{isBasketShow && <Basket />}
				{toast !== '' && <Toast message={toast} />}
			</div>
		</GoodsLayout>
	);
};
export default GoodsPage;


Если делать по правилам из документации, то есть, в файле root.jsx оборачивать в Проблема в том, что у меня будут множество страниц, и хочется использовать контекст только на этой.
Сам контекст не может быть использован на самой странице, по-этому и создал layout, только с ним не работает страница GoodsPage.jsx
Uncaught TypeError: Cannot destructure property 'toast' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.
    at GoodsPage (GoodsPage.jsx:13:1)


По-ходу, что-то не так делаю?
  • Вопрос задан
  • 230 просмотров
Подписаться 1 Простой 1 комментарий
Решения вопроса 1
@marselo777
React developer
Вы используете контекст до его инициализации. Контекст доступен для всех дочерних компонентов находящихся в GoodsLayout, вам нужно обернуть GoodsPage в GoodsLayout, либо использовать хук useContext в дочерних компонентах для GoodsLayout.
Т.е чтобы контекст работал в вашем случае, нужно сделать так:

<GoodsLayout>
  <GoodsPage/>
</GoodsLayout>
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы