-Hello guys , I've encountered a problem which taken a lot time in research but I did not get a solution:
->here I have 2 separate components header.jsx(contains booking.com homepage clone) and hotel.jsx and I want to send a state containing search data from header to hotel using context and use reducer . I've declared a separate context file searchcontext.js ,exported the context and its provider , wrapped the app using the provider in index.js , and I've called the dispatch function in header.js in order to send the needed state , when I call useContext in hotel.js to get the state the console return undefined ,any help please :)
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { SearchContextProvider } from './context/searchcontext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<SearchContextProvider>
<App />
</SearchContextProvider>
</React.StrictMode>
);
searchcontext.js
import { createContext, useReducer } from "react";
const INITIAL_STATE = {
city: undefined,
dates: [],
options: {
adult: undefined,
children: undefined,
room: undefined,
},
};
export const SearchContext = createContext(INITIAL_STATE);
const searchReducer = (action, state) => {
switch (action.type) {
case "NEW_SEARCH":
return action.payload;
case "RESET_SEARCH":
return INITIAL_STATE;
default:
return state;
}
};
export const SearchContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(searchReducer, INITIAL_STATE);
return (
<SearchContext.Provider
value={{
city: state.city,
dates: state.dates,
options: state.options,
dispatch,
}}
>
{children}
</SearchContext.Provider>
);
};
header.jsx file
import "./header.css";
import { DateRange } from "react-date-range";
import { useContext, useState } from "react";
import "react-date-range/dist/styles.css"; // main css file
import "react-date-range/dist/theme/default.css"; // theme css file
import { format } from "date-fns";
import { useNavigate } from "react-router-dom";
import {SearchContext} from "../../context/searchcontext";
const Header = ({ type }) => {
const [destination, setDestination] = useState("");
const [openDate, setOpenDate] = useState(false);
const [dates, setDates] = useState([
{
startDate: new Date(),
endDate: new Date(),
key: "selection",
},
]);
const [openOptions, setOpenOptions] = useState(false);
const [options, setOptions] = useState({
adult: 1,
children: 0,
room: 1,
});
const navigate = useNavigate();
const handleOption = (name, operation) => {
setOptions((prev) => {
return {
...prev,
[name]: operation === "i" ? options[name] 1 : options[name] - 1,
};
});
};
const{dispatch}= useContext(SearchContext);
const handleSearch = () => {
dispatch({type:"NEW_SEARCH",payload:{destination,dates,options}});
console.log(true)
navigate("/hotels", { state: { destination, dates, options } });
};
return (
<div className="header">
<div className="headerContainer" >
<div className="headerSearch">
<div className="headerSearchItem">
<input
type="text"
placeholder="where are you going?"
className="headerSearchInput"
onChange={(e) => setDestination(e.target.value)}
/>
</div>
<div className="headerSearchItem">
<span
onClick={() => setOpenDate(!openDate)}
className="headerSearchText"
>
{`${format(dates[0].startDate, "dd/MM/yyyy")} to ${format(
dates[0].endDate,
"dd/MM/yyyy"
)}`}
</span>
{openDate && (
<DateRange
editableDateInputs={true}
onChange={(item) => setDates([item.selection])}
moveRangeOnFirstSelection={false}
ranges={dates}
minDate={new Date()}
className="date"
/>
)}
</div>
<div className="headerSearchItem">
<span
onClick={() => setOpenOptions(!openOptions)}
className="headerSearchText">
{`${options.adult}`} adult {`${options.children}`} children
{`${options.room}`} room
</span>
{openOptions && (
<div className="options">
<div className="option">
<span className="optionTitle">Adults</span>
<div className="optionCounter">
<button
className="optionCounterButton"
onClick={() => {
handleOption("adult", "d");
}}
disabled={options.adult <= 1}
>
-
</button>
<span>{options.adult}</span>
<button
className="optionCounterButton"
onClick={() => {
handleOption("adult", "i");
}}
>
</button>
</div>
</div>
<div className="option">
<span className="optionTitle">Children</span>
<div className="optionCounter">
<button
className="optionCounterButton"
onClick={() => {
handleOption("children", "d");
}}
disabled={options.children <= 0}
>
-
</button>
<span>{options.children}</span>
<button
className="optionCounterButton"
onClick={() => {
handleOption("children", "i");
}}
>
</button>
</div>
</div>
<div className="option">
<span className="optionTitle">Rooms</span>
<div className="optionCounter">
<button
className="optionCounterButton"
onClick={() => {
handleOption("room", "d");
}}
disabled={options.room <= 1}
>
-
</button>
<span>{options.room}</span>
<button
className="optionCounterButton"
onClick={() => {
handleOption("room", "i");
}}
>
</button>
</div>
</div>
</div>
)}
</div>
<div className="headerSearchItem">
<button className="headerBtn" onClick={handleSearch}>
Search
</button>
</div>
</div>
</>
)}
</div>
</div>
);
};
export default Header;
hotel.jsx file
import "./hotel.css";
import { useState } from "react";
import { useContext } from "react";
import { SearchContext } from "../../context/searchcontext";
const Hotel = () => {
const [sliderNumber, setSliderNumber] = useState(0);
const [openSlider, setOpenSlider] = useState(false);
//here where the problem encouters
const {state}= useContext(SearchContext);
console.log(state);//returns undefined
return (
<div style={{ margin: -8 }}>
<div className="hotelItemContainer">
<div className="hotelItemWrapper">
<button className="hotelBookBtn"> Reserve or Book Now!</button>
<h1 className="hotelTitle">lorem</h1>
<span className="hotelSpecFeatures">
You're eligible for a Genius discount! , to save at this property
, all you have to do is sign in.
</span>
</div>
</div>
</div>
);
};
export default Hotel;
CodePudding user response:
In your hotel.js you are expecting a state
property. However you have not been exposing one in your search context
searchcontext.js
Your context was not exposing a state
export const SearchContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(searchReducer, INITIAL_STATE);
return (
<SearchContext.Provider
value={{
city: state.city,
dates: state.dates,
options: state.options,
state, // you forgot to add this
dispatch,
}}
>
{children}
</SearchContext.Provider>
);
};
alternatively don't use the state property in hotel.js instead use the destructure values that are exposed in your context.