У меня есть пользовательский хук, использующий useReducer, который контролирует, какие компоненты отображаются на панели инструментов. Он работает, как и ожидалось в родительском, но когда я использую его в дочернем, запускается useReducer, состояние изменяется, но не изменяется в родительском компоненте, поэтому он не перерисовывается с соответствующими изменениями. Я использую оператор распространения в моем редукторе, чтобы вернуть новый объект. Я пробовал несколько хакерских вещей с дополнительными useStates и useEffects внутри хука, но ни один из них не оказал влияния. Я пробовал разные уровни деструктуризации, тоже безрезультатно. Я вижу, как состояние изменяется, но кажется, что оно не распознается как новый объект при возвращении в родительский объект.
Настраиваемый хук
import { useReducer } from "react"
let dashboard = {}
export const dashboardReducer = (state, action) => {
console.log("dashboardReducer: state, action=", state, action)
switch (action.component) {
case 'lowerNav':
switch (action.label) {
case 'journal':
return { ...state, lowerNav: 'journal'}
case 'progress':
return { ...state, lowerNav: 'progress'}
case 'badges':
return { ...state, lowerNav: 'badges'}
case 'challenges':
return { ...state, lowerNav: 'challenges'}
case 'searchResults':
return { ...state, lowerNav: 'searchResults'}
}
case 'foodSearchBox' :
if (action.searchResults) {
return { ...state, searchResults: action.searchResults, lowerNav: "searchResults"}
} else {
return { ...state, searchResults: "NO SEARCH RESULTS"}
}
default:
throw new Error()
}
}
export const useDashboard = () => {
const [ state, dispatch ] = useReducer(dashboardReducer, dashboard)
//fires on every dispatch no matter which component
console.log("useDashboard: state=", state)
return [ state, dispatch ]
}
export const initDashboard = initialState => {
dashboard = initialState
}
Родительский компонент
const Dashboard = () => {
initDashboard({ lowerNav: "journal", log: "daily", meal: "breakfast" });
const [ state, dispatch ] = useDashboard();
const lowerNav = state.lowerNav
useEffect(() => {
console.log("Dashboard: useEffect: state=", state) //Fires when a dispatch is run from this component, but not from any other components
}, [state])
const dateOptions = { year: "numeric", month: "long", day: "numeric" };
const currentDate = new Date(Date.now());
return (
<Layout>
<div className="flex">
<DashUser />
<div className="flex-1"></div>
<div className="flex-1 px-32 self-center">
<FoodSearchBox />
</div>
</div>
<nav className="flex bg-mobileFoot">
<div className="flex-1"></div>
<ul className="flex-1 flex justify-around text-lg font-medium py-2">
<li
className={`${
lowerNav === "journal" ? "border-b-2 border-pink-500" : ""
} cursor-pointer`}
value="journal"
onClick={() =>
dispatch({ component: "lowerNav", label: "journal" })
}
>
Food Journal
</li>
<li
className={`${
lowerNav === "progress" ? "border-b-2 border-pink-500" : ""
} cursor-pointer`}
value="progress"
onClick={() =>
dispatch({ component: "lowerNav", label: "progress" })
}
>
Progress
</li>
<li
className={`${
lowerNav === "badges" ? "border-b-2 border-pink-500" : ""
} cursor-pointer`}
value="badges"
onClick={() => dispatch({ component: "lowerNav", label: "badges" })}
>
Badges
</li>
<li
className={`${
lowerNav === "challenges" ? "border-b-2 border-pink-500" : ""
} cursor-pointer`}
value="challenges"
onClick={() =>
dispatch({ component: "lowerNav", label: "challenges" })
}
>
Challenges
</li>
</ul>
<span className="flex flex-1 text-sm justify-end items-center">
<time className="pr-32">
{currentDate.toLocaleString("en-US", dateOptions)}
</time>
</span>
</nav>
<div className="flex py-4">
<DailyVibe />
<div className="flex-1"></div>
<div className="border border-black mr-32 ml-6">Macro Charts</div>
</div>
<div className="ml-20 mr-32">
{lowerNav === "journal" ? (
<DesktopFoodJournal />
) : lowerNav === "progress" ? (
<Progress />
) : lowerNav === "badges" ? (
"Badges"
) : lowerNav === "challenges" ? (
"Challenges"
) : lowerNav === "searchResults" ? (
<FoodSearchResults />
) : (
"Error"
)}
</div>
</Layout>
);
}
export default withApollo(Dashboard)
Дочерний компонент
import { useState } from "react";
import { foodDbSearch } from "../../lib/edamam.js";
import { useDashboard } from "../../lib/hooks/useDashboard.js";
export default function FoodSearchBox() {
const [item, setItem] = useState("");
const dispatch = useDashboard()[1];
const handleChange = e => {
setItem(e.target.value);
};
const query = item.replace(" ", "%20"); // Format the entered food item for the API call
const handleSubmit = async e => {
e.preventDefault();
const list = await foodDbSearch(query); // Hit the foodDB API
// Add the search results to dashboard state
dispatch({ component: "foodSearchBox", searchResults: list.hints });
setItem('') // Reset input
};
return (
<form onSubmit={handleSubmit}>
<input
className="w-full border border-purple-400 rounded focus:border-purple-200 px-4 py-2"
type="text"
placeholder="Search Food Item"
name="food"
value={item}
onChange={handleChange}
/>
</form>
);
}
Я пытаюсь выполнить рефакторинг из того, что изначально было много грязного пропеллера, с помощью хуков useState, распространяемых повсеместно. Я использую Next. js и пытаюсь избежать использования Context API или добавления Redux. Мне действительно нужно сохранять состояние только для отдельных компонентов страницы, и это на самом деле просто локальное состояние пользовательского интерфейса, поскольку я обрабатываю большую часть данных с помощью аполлон-хуков.