mirror of
https://github.com/rjNemo/meal_planner
synced 2026-06-06 02:26:49 +00:00
state (#9)
* configure context * refactor * get meal with context * random button with context * async actions * refactor meal client
This commit is contained in:
parent
7cde13f071
commit
e8ac939fc9
21 changed files with 175 additions and 183 deletions
22
src/App.tsx
22
src/App.tsx
|
|
@ -1,24 +1,13 @@
|
|||
import { FC, useState } from "react";
|
||||
import { FC } from "react";
|
||||
import { PreLoader } from "./components/PreLoader";
|
||||
import "./index.css";
|
||||
import MainLayout from "./layouts/MainLayout";
|
||||
import { AppRouter } from "./router";
|
||||
import { Router } from "./router/Router";
|
||||
import { getData } from "./services/api";
|
||||
import { MealSummary } from "./types/meal";
|
||||
import { useAuth0 } from "./utils/auth0-spa";
|
||||
|
||||
export const App: FC = () => {
|
||||
const { loading } = useAuth0();
|
||||
const [searchString, setSearchString] = useState("");
|
||||
const [searchResults, setSearchResults] = useState({
|
||||
meals: [] as MealSummary[],
|
||||
});
|
||||
const [_, setMeal] = useState(null);
|
||||
|
||||
const getRandomMeal = () => {
|
||||
getData("random", setMeal);
|
||||
};
|
||||
|
||||
return loading ? (
|
||||
<div className="container center-align valign-wrapper">
|
||||
|
|
@ -26,13 +15,8 @@ export const App: FC = () => {
|
|||
</div>
|
||||
) : (
|
||||
<Router>
|
||||
<MainLayout
|
||||
getRandomMeal={getRandomMeal}
|
||||
searchString={searchString}
|
||||
setSearchResults={setSearchResults}
|
||||
setSearchString={setSearchString}
|
||||
>
|
||||
<AppRouter searchString={searchString} searchResults={searchResults} />
|
||||
<MainLayout>
|
||||
<AppRouter />
|
||||
</MainLayout>
|
||||
</Router>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@ import { Logo } from "./Logo";
|
|||
import { LogOutButton } from "./LogOutButton";
|
||||
import { RandomButton } from "./RandomButton";
|
||||
|
||||
type Props = {
|
||||
openNavClick: React.MouseEventHandler;
|
||||
handleClick: () => void;
|
||||
};
|
||||
type Props = { openNavClick: React.MouseEventHandler };
|
||||
|
||||
export const Navbar: FC<Props> = ({ openNavClick, handleClick }) => {
|
||||
export const Navbar: FC<Props> = ({ openNavClick }) => {
|
||||
const { isAuthenticated } = useAuth0();
|
||||
|
||||
return (
|
||||
|
|
@ -31,7 +28,6 @@ export const Navbar: FC<Props> = ({ openNavClick, handleClick }) => {
|
|||
)}
|
||||
<li>
|
||||
<RandomButton
|
||||
handleClick={handleClick}
|
||||
url={buttonURL}
|
||||
size="small"
|
||||
color="orange darken-2"
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
import { FC } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useMeal } from "../store/meal";
|
||||
import { fetchRandomMeal } from "../store/meal/async";
|
||||
|
||||
type Props = {
|
||||
url: string;
|
||||
size?: string;
|
||||
handleClick: () => void;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export const RandomButton: FC<Props> = ({
|
||||
url,
|
||||
size = "large",
|
||||
handleClick,
|
||||
color,
|
||||
}) => {
|
||||
export const RandomButton: FC<Props> = ({ url, size = "large", color }) => {
|
||||
const classString = `waves-effect waves-light btn-${size} ${color}`;
|
||||
const { dispatch } = useMeal();
|
||||
return (
|
||||
<Link to={url}>
|
||||
<button className={classString} onClick={handleClick}>
|
||||
<button className={classString} onClick={() => fetchRandomMeal(dispatch)}>
|
||||
Random Recipe
|
||||
</button>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,20 @@
|
|||
import React, { ChangeEvent, FC } from "react";
|
||||
import React, { ChangeEvent, FC, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getData } from "../services/api";
|
||||
import { MealSummary } from "../types/meal";
|
||||
import { useMeal } from "../store/meal";
|
||||
import { fetchSearchResults } from "../store/meal/async";
|
||||
|
||||
type Props = {
|
||||
searchString: string;
|
||||
setSearchString: React.Dispatch<React.SetStateAction<string>>;
|
||||
setSearchResults: React.Dispatch<
|
||||
React.SetStateAction<{ meals: MealSummary[] }>
|
||||
>;
|
||||
};
|
||||
|
||||
export const SearchBar: FC<Props> = ({
|
||||
searchString,
|
||||
setSearchString,
|
||||
setSearchResults,
|
||||
}) => {
|
||||
export const SearchBar: FC = () => {
|
||||
const { dispatch } = useMeal();
|
||||
const [searchString, setSearchString] = useState("");
|
||||
const getSearchResults: React.MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
searchString === ""
|
||||
? e.preventDefault()
|
||||
: getData(searchString, setSearchResults, "search");
|
||||
: fetchSearchResults(dispatch, searchString);
|
||||
};
|
||||
|
||||
const clearSearchBar = () => {
|
||||
setSearchString("");
|
||||
setSearchResults({ meals: [] });
|
||||
dispatch({ type: "clearSearchResults" });
|
||||
};
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
|
|
|
|||
|
|
@ -9,13 +9,9 @@ import { LogInButton } from "./LogInButton";
|
|||
import { LogOutButton } from "./LogOutButton";
|
||||
import { RandomButton } from "./RandomButton";
|
||||
|
||||
type Props = {
|
||||
showNav: boolean;
|
||||
closeNavClick: React.MouseEventHandler;
|
||||
handleClick: () => void;
|
||||
};
|
||||
type Props = { showNav: boolean; closeNavClick: React.MouseEventHandler };
|
||||
|
||||
export const SideNav: FC<Props> = ({ showNav, closeNavClick, handleClick }) => {
|
||||
export const SideNav: FC<Props> = ({ showNav, closeNavClick }) => {
|
||||
const { isAuthenticated, user } = useAuth0();
|
||||
let transformStyle = {
|
||||
transform: showNav ? "translateX(0%)" : "translateX(-105%)",
|
||||
|
|
@ -65,12 +61,7 @@ export const SideNav: FC<Props> = ({ showNav, closeNavClick, handleClick }) => {
|
|||
</li>
|
||||
|
||||
<li>
|
||||
<RandomButton
|
||||
handleClick={handleClick}
|
||||
url={buttonURL}
|
||||
size="small"
|
||||
color="orange darken-2"
|
||||
/>
|
||||
<RandomButton url={buttonURL} size="small" color="orange darken-2" />
|
||||
</li>
|
||||
<li>
|
||||
<Link to="#">
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export const apiRoot = "https://www.themealdb.com/api/json/v1/1/";
|
||||
export const buttonURL = "/random";
|
||||
export const links = ["categories", "contact"];
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const Categories = () => {
|
|||
const [categories, setCategories] = useState({ categories: [] });
|
||||
|
||||
const getCategories = () => {
|
||||
getData("categories", setCategories);
|
||||
getData("categories").then((data) => setCategories(data));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ export const Category: FC = () => {
|
|||
const [meals, setMeals] = useState({ meals: [] });
|
||||
|
||||
useEffect(() => {
|
||||
const getMeals = () => getData(strCategory, setMeals, "filter");
|
||||
getMeals();
|
||||
const getMeals = () => getData(strCategory, "filter");
|
||||
getMeals().then((data) => setMeals(data));
|
||||
}, [strCategory]);
|
||||
|
||||
return !meals.meals ? (
|
||||
|
|
|
|||
|
|
@ -8,12 +8,7 @@ export const Home: FC = () => (
|
|||
<div className="row">
|
||||
<div className="col s12 m6">
|
||||
<h1 className="logo">Chef's Online Cookbook</h1>
|
||||
<RandomButton
|
||||
url={buttonURL}
|
||||
size="large"
|
||||
color="orange darken-2"
|
||||
handleClick={() => {}}
|
||||
/>
|
||||
<RandomButton url={buttonURL} size="large" color="orange darken-2" />
|
||||
</div>
|
||||
<picture className="col s12 m6">
|
||||
<img src={HeroImage} alt="hero_image" width="100%" />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { FC, useEffect, useState } from "react";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getData } from "../../services/api";
|
||||
import { useFirebase } from "../../services/Firebase";
|
||||
import { MealApi as MealType } from "../../types/meal";
|
||||
import { useMeal } from "../../store/meal";
|
||||
import { fetchMeal, fetchRandomMeal } from "../../store/meal/async";
|
||||
import { useAuth0 } from "../../utils/auth0-spa";
|
||||
import { NotFound } from "../NotFound";
|
||||
import { MealPage } from "./components/MealPage";
|
||||
|
|
@ -12,31 +12,18 @@ export const Meal: FC = () => {
|
|||
// hooks
|
||||
const { user, isAuthenticated } = useAuth0();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { state, dispatch } = useMeal();
|
||||
const fb = useFirebase();
|
||||
// local state
|
||||
const [meal, setMeal] = useState({ meals: [] as MealType[] });
|
||||
const [isFav, setIsFav] = useState<boolean>();
|
||||
// variables
|
||||
const mealItem = meal?.meals?.[0];
|
||||
|
||||
const getMeal = (
|
||||
id: string,
|
||||
setMeal: React.Dispatch<React.SetStateAction<{ meals: MealType[] }>>
|
||||
) => {
|
||||
getData(id, setMeal, "lookup");
|
||||
};
|
||||
|
||||
const getRandomMeal = (
|
||||
setMeal: React.Dispatch<React.SetStateAction<{ meals: MealType[] }>>
|
||||
) => {
|
||||
getData("random", setMeal);
|
||||
};
|
||||
|
||||
const mealItem = state.meals?.[0];
|
||||
// effects
|
||||
/** Fetch meal from db */
|
||||
useEffect(() => {
|
||||
!id ? getRandomMeal(setMeal) : getMeal(id, setMeal);
|
||||
}, [id]);
|
||||
!id ? fetchRandomMeal(dispatch) : fetchMeal(dispatch, id);
|
||||
}, [id, dispatch]);
|
||||
|
||||
/** Updates fav status in db */
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
|
|
@ -63,7 +50,7 @@ export const Meal: FC = () => {
|
|||
const item = buildMealProps(mealItem, isFav!);
|
||||
const ingredients = buildIngredientList(mealItem);
|
||||
|
||||
return !!meal?.meals ? (
|
||||
return !!state.meals ? (
|
||||
<MealPage
|
||||
meal={item}
|
||||
ingredients={ingredients}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const NotFound: FC = () => (
|
|||
/>
|
||||
</div>
|
||||
<div className="card-content">
|
||||
<RandomButton url="/random" handleClick={() => {}} />
|
||||
<RandomButton url="/random" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,29 +6,26 @@ import { SearchResult } from "./SearchResult";
|
|||
|
||||
type Props = {
|
||||
searchString: string;
|
||||
searchResults: { meals: MealSummary[] };
|
||||
searchResults: MealSummary[];
|
||||
};
|
||||
|
||||
export const SearchPage: FC<Props> = ({ searchString, searchResults }) => {
|
||||
const { meals } = searchResults;
|
||||
return (
|
||||
<PageLayout title={`Results for: ${searchString}`}>
|
||||
{!meals ? (
|
||||
<div className="center-align">
|
||||
<p>
|
||||
No results to display, instead there is a picture of my breakfast.
|
||||
</p>
|
||||
<img src={BreakfastImage} alt="Nothing here!" width="70%" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
<ul>
|
||||
{meals.map((meal, i) => (
|
||||
<SearchResult key={i} meal={meal} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
export const SearchPage: FC<Props> = ({ searchString, searchResults }) => (
|
||||
<PageLayout title={`Results for: ${searchString}`}>
|
||||
{!searchResults ? (
|
||||
<div className="center-align">
|
||||
<p>
|
||||
No results to display, instead there is a picture of my breakfast.
|
||||
</p>
|
||||
<img src={BreakfastImage} alt="Nothing here!" width="70%" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
<ul>
|
||||
{searchResults.map((meal, i) => (
|
||||
<SearchResult key={i} meal={meal} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { FC } from "react";
|
||||
import { MealSummary } from "../../types/meal";
|
||||
import { useMeal } from "../../store/meal";
|
||||
import { SearchPage } from "./components/SearchPage";
|
||||
|
||||
type Props = {
|
||||
searchString: string;
|
||||
searchResults: { meals: MealSummary[] };
|
||||
export const Search: FC = () => {
|
||||
const { state } = useMeal();
|
||||
return (
|
||||
<SearchPage
|
||||
searchString={state.searchString}
|
||||
searchResults={state.search}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Search: FC<Props> = ({ searchString, searchResults }) => (
|
||||
<SearchPage searchString={searchString} searchResults={searchResults} />
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import * as serviceWorker from "./serviceWorker";
|
|||
import { Auth0Provider } from "./utils/auth0-spa";
|
||||
import history from "./utils/history";
|
||||
import { FirebaseContext } from "./services/Firebase";
|
||||
import { AppProvider } from "./store/meal";
|
||||
|
||||
const onRedirectCallBack = (appState) => {
|
||||
history.push(
|
||||
|
|
@ -24,7 +25,9 @@ ReactDOM.render(
|
|||
>
|
||||
{/*<FirebaseContext.Provider value={new Firebase()}> todo fix Firebase app*/}
|
||||
<FirebaseContext.Provider>
|
||||
<App />
|
||||
<AppProvider>
|
||||
<App />
|
||||
</AppProvider>
|
||||
</FirebaseContext.Provider>
|
||||
</Auth0Provider>,
|
||||
document.getElementById("root")
|
||||
|
|
|
|||
|
|
@ -3,23 +3,8 @@ import { Footer } from "../components/Footer";
|
|||
import { Navbar } from "../components/Navbar";
|
||||
import { SearchBar } from "../components/SearchBar";
|
||||
import { SideNav } from "../components/SideNav";
|
||||
import { MealSummary } from "../types/meal";
|
||||
|
||||
type Props = {
|
||||
getRandomMeal: () => void;
|
||||
searchString: string;
|
||||
setSearchString: React.Dispatch<React.SetStateAction<string>>;
|
||||
setSearchResults: React.Dispatch<
|
||||
React.SetStateAction<{ meals: MealSummary[] }>
|
||||
>;
|
||||
};
|
||||
const MainLayout: FC<Props> = ({
|
||||
getRandomMeal,
|
||||
searchString,
|
||||
setSearchString,
|
||||
setSearchResults,
|
||||
children,
|
||||
}) => {
|
||||
const MainLayout: FC = ({ children }) => {
|
||||
const [showNav, setShowNav] = useState(false);
|
||||
|
||||
const openNavClick: React.MouseEventHandler = (e) => {
|
||||
|
|
@ -43,18 +28,9 @@ const MainLayout: FC<Props> = ({
|
|||
return (
|
||||
<>
|
||||
<header>
|
||||
<Navbar handleClick={getRandomMeal} openNavClick={openNavClick} />
|
||||
|
||||
<SearchBar
|
||||
searchString={searchString}
|
||||
setSearchString={setSearchString}
|
||||
setSearchResults={setSearchResults}
|
||||
/>
|
||||
<SideNav
|
||||
showNav={showNav}
|
||||
closeNavClick={closeNavClick}
|
||||
handleClick={() => {}}
|
||||
/>
|
||||
<Navbar openNavClick={openNavClick} />
|
||||
<SearchBar />
|
||||
<SideNav showNav={showNav} closeNavClick={closeNavClick} />
|
||||
</header>
|
||||
<main>{children}</main>
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { FC } from "react";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
};
|
||||
type Props = { title: string };
|
||||
|
||||
const PageLayout: FC<Props> = ({ title, children }) => (
|
||||
<div className="container">
|
||||
|
|
|
|||
|
|
@ -3,20 +3,15 @@ import { Redirect, Route, Switch } from "react-router-dom";
|
|||
import { buttonURL } from "../constants";
|
||||
import { Categories } from "../containers/Categories";
|
||||
import { Category } from "../containers/Category";
|
||||
import { Contact } from "../containers/Contact";
|
||||
import { Home } from "../containers/Home";
|
||||
import { Meal } from "../containers/Meal";
|
||||
import { NotFound } from "../containers/NotFound";
|
||||
import { Profile } from "../containers/Profile";
|
||||
import { Search } from "../containers/Search";
|
||||
import { Contact } from "../containers/Contact";
|
||||
import { NotFound } from "../containers/NotFound";
|
||||
import { MealSummary } from "../types/meal";
|
||||
import { PrivateRoute } from "./PrivateRoute";
|
||||
|
||||
type Props = {
|
||||
searchString: string;
|
||||
searchResults: { meals: MealSummary[] };
|
||||
};
|
||||
const AppRouter: FC<Props> = ({ searchString, searchResults }) => (
|
||||
const AppRouter: FC = () => (
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<Home />
|
||||
|
|
@ -37,7 +32,7 @@ const AppRouter: FC<Props> = ({ searchString, searchResults }) => (
|
|||
</Route>
|
||||
|
||||
<Route exact path="/search">
|
||||
<Search searchString={searchString} searchResults={searchResults} />
|
||||
<Search />
|
||||
</Route>
|
||||
|
||||
<Route path="/contact">
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
import React from "react";
|
||||
import { apiRoot } from "../constants";
|
||||
|
||||
export const createURI = (keyword: string, option?: string) => {
|
||||
const ROOT = "https://www.themealdb.com/api/json/v1/1/";
|
||||
type Option = "filter" | "lookup" | "search";
|
||||
|
||||
const createURI = (keyword: string, option?: Option) => {
|
||||
if (!option) {
|
||||
return `${ROOT}${keyword}.php`;
|
||||
return `${apiRoot}${keyword}.php`;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case "filter": {
|
||||
return `${ROOT}${option}.php?c=${keyword}`;
|
||||
return `${apiRoot}${option}.php?c=${keyword}`;
|
||||
}
|
||||
case "lookup": {
|
||||
return `${ROOT}${option}.php?i=${keyword}`;
|
||||
return `${apiRoot}${option}.php?i=${keyword}`;
|
||||
}
|
||||
case "search": {
|
||||
return `${ROOT}${option}.php?s=${keyword}`;
|
||||
return `${apiRoot}${option}.php?s=${keyword}`;
|
||||
}
|
||||
default: {
|
||||
throw Error("Unexpected URI");
|
||||
|
|
@ -22,15 +23,10 @@ export const createURI = (keyword: string, option?: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getData = (
|
||||
keyword: string,
|
||||
set: React.Dispatch<React.SetStateAction<any>>,
|
||||
option?: string
|
||||
) => {
|
||||
export const getData = (keyword: string, option?: Option) => {
|
||||
const URI = createURI(keyword, option);
|
||||
|
||||
fetch(URI)
|
||||
return fetch(URI)
|
||||
.then((response) => response.json())
|
||||
.catch((error) => console.warn(error + "url:" + URI))
|
||||
.then((data) => set(data));
|
||||
.catch((error) => console.warn(error + "url:" + URI));
|
||||
};
|
||||
|
|
|
|||
23
src/store/meal/async.ts
Normal file
23
src/store/meal/async.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { getData } from "../../services/api";
|
||||
import { Dispatch } from "./reducer";
|
||||
|
||||
export const fetchRandomMeal = async (dispatch: Dispatch) => {
|
||||
const meal = await getData("random");
|
||||
dispatch({ type: "setMeal", payload: meal?.meals });
|
||||
};
|
||||
|
||||
export const fetchMeal = async (dispatch: Dispatch, id: string) => {
|
||||
const meal = await getData(id, "lookup");
|
||||
dispatch({ type: "setMeal", payload: meal?.meals });
|
||||
};
|
||||
|
||||
export const fetchSearchResults = async (
|
||||
dispatch: Dispatch,
|
||||
searchString: string
|
||||
) => {
|
||||
const meals = await getData(searchString, "search");
|
||||
dispatch({
|
||||
type: "setSearchResults",
|
||||
payload: { search: meals?.meals, searchString },
|
||||
});
|
||||
};
|
||||
35
src/store/meal/index.tsx
Normal file
35
src/store/meal/index.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//https://kentcdodds.com/blog/how-to-use-react-context-effectively
|
||||
|
||||
import { createContext, FC, useContext, useReducer } from "react";
|
||||
import { MealApi, MealSummary } from "../../types/meal";
|
||||
import { appReducer, Dispatch } from "./reducer";
|
||||
|
||||
export type AppState = {
|
||||
meals: MealApi[];
|
||||
search: MealSummary[];
|
||||
searchString: string;
|
||||
};
|
||||
|
||||
const initState = {
|
||||
meals: [] as MealApi[],
|
||||
search: [] as MealSummary[],
|
||||
searchString: "",
|
||||
};
|
||||
|
||||
type ContextType = { state: AppState; dispatch: Dispatch } | undefined;
|
||||
|
||||
const AppContext = createContext<ContextType>(undefined);
|
||||
|
||||
export const useMeal = () => {
|
||||
const context = useContext(AppContext);
|
||||
if (!context) {
|
||||
throw new Error("useMeal must be used within a AppProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export const AppProvider: FC = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(appReducer, initState);
|
||||
const value = { state, dispatch };
|
||||
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
||||
};
|
||||
27
src/store/meal/reducer.ts
Normal file
27
src/store/meal/reducer.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { MealSummary } from "../../types/meal";
|
||||
import { AppState } from "./index";
|
||||
|
||||
export const appReducer = (state: AppState, action: Action) => {
|
||||
switch (action.type) {
|
||||
case "setMeal":
|
||||
return { ...state, meals: action.payload };
|
||||
case "setSearchResults":
|
||||
return {
|
||||
...state,
|
||||
search: action.payload.search,
|
||||
searchString: action.payload.searchString,
|
||||
};
|
||||
case "clearSearchResults":
|
||||
return { ...state, search: [] as MealSummary[] };
|
||||
default: {
|
||||
throw new Error(`Unhandled action type: ${action.type}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export type Action = {
|
||||
payload?: any;
|
||||
type: "setMeal" | "setSearchResults" | "clearSearchResults";
|
||||
};
|
||||
|
||||
export type Dispatch = (action: Action) => void;
|
||||
Loading…
Reference in a new issue