Home > Back-end >  useReducer returns undefined
useReducer returns undefined

Time:10-21

-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.

  • Related