我有一个使用useReducer的自定义钩子,用于控制在仪表板上呈现哪些组件。它在父级中按预期方式工作,但是当我在子级中使用它时,useReducer运行,状态已更改,但父级组件中状态未更改,因此不会使用适当的更改重新呈现。我在我的减速器中使用了散布运算符来返回一个新对象。我已经尝试了一些带有附加useStates和useEffects的hacky东西,但是它们都没有影响。我尝试了不同程度的破坏,也没有效果。我可以看到状态发生了变化,但是当在父对象中返回状态时,似乎并没有将其识别为新对象。
自定义挂钩
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。我真的只需要在单个页面组件上保持状态,而这实际上只是本地UI状态,因为我正在使用apollo-hooks处理大多数数据。
答案 0 :(得分:3)
您在子组件中调用的调度功能与父组件中的调度功能不同,并且不会更新相同的状态。 public static void main(String[] args){
int arr[] = {1,2,3,4,5};
int sum = sumArray(arr);
System.out.println("The sum is: "+ sum);
}
public static int sumArray(int[] inputArray){
int sum = 0;
for (int i = 0; i < inputArray.length; i++){
sum += inputArray[i];
return sum;
}
}
挂钩的不同用法返回不同的(状态,调度)对,它们不会互相影响。
如果您希望父组件的状态可以从子组件更改,但又不想使用上下文API,则必须将父组件的调度功能(或使用它的回调)传递给子组件组件作为道具。
useDashboard