javascript - React 状态数组似乎恢复到原始状态
问题描述
我有一个像这样的状态数组:
const [menusActive, setMenusActive] = useState([
"MainMenu"
]);
它跟踪可见的菜单。我的方法是向函数toggleArrayItem()发送一个菜单键/名称,如果它不存在则将键添加到数组中,如果存在则将其删除:
function toggleArrayItem(arr, item) {
console.log('item: ', item);
console.log('arr: ', arr);
let returnArray = [];
if (arr.includes(item) === true) {
console.log('removing item: ', item);
returnArray = arr.filter(i => i !== item) // remove item
} else {
console.log('adding item: ', item);
returnArray = [ ...arr, item ]; // add item
}
console.log('returnArray: ', returnArray);
return returnArray;
}
单击菜单项时,它会执行以下操作:
let existingMenu = [...menusActive];
console.log('menu were sending to toggleArrayItem is: ', existingMenu);
let updatedMenu = toggleArrayItem(existingMenu, menuName);
setMenusActive(updatedMenu);
在调试时,我通过以下方式检查menuActive更新:
useEffect(() => {
console.log('menusActive after: ', menusActive);
}, [menusActive]);
控制台输出在第一次菜单单击时有意义...
menu were sending to toggleArrayItem is: ["MainMenu"]
MenuDock.js:36 item: SubMenu
MenuDock.js:37 arr: ["MainMenu"]
MenuDock.js:43 adding item: SubMenu
MenuDock.js:46 returnArray: (2) ["MainMenu", "SubMenu"]
MenuDock.js:54 menusActive after: (2) ["MainMenu", "SubMenu"]
但是当我再次单击相同的菜单项时不会:
menu were sending to toggleArrayItem is: ["MainMenu"]
MenuDock.js:36 item: SubMenu
MenuDock.js:37 arr: ["MainMenu"]
MenuDock.js:43 adding item: SubMenu
MenuDock.js:46 returnArray: (2) ["MainMenu", "SubMenu"]
MenuDock.js:54 menusActive after: (2) ["MainMenu", "SubMenu"]
第二个数组项“SubMenu”在我尝试第二次菜单单击之前被声明为存在于menusActive中发生了什么(我认为这会删除“SubMenu”而不是再次尝试添加它)?
全组件
我在上面总结了我认为相关的内容,以免压倒一切。如果我遗漏了任何有用的东西,这里是完整的组件:
export const MenuDock = ({ side, menuJSON }) => {
const [menuListItemArray, setMenuListItemArray] = useState([]);
const [menusActive, setMenusActive] = useState([
"MainMenu"
]);
function handleItemClick(menuName) {
if (menuName !== "none") {
let existingMenu = [...menusActive];
console.log('menu were sending to toggleArrayItem is: ', existingMenu);
let updatedMenu = toggleArrayItem(existingMenu, menuName);
setMenusActive(updatedMenu);
}
}
function toggleArrayItem(arr, item) {
console.log('item: ', item);
console.log('arr: ', arr);
let returnArray = [];
if (arr.includes(item) === true) {
console.log('removing item: ', item);
returnArray = arr.filter(i => i !== item) // remove item
} else {
console.log('adding item: ', item);
returnArray = [ ...arr, item ]; // add item
}
console.log('returnArray: ', returnArray);
return returnArray;
}
useEffect(() => {
// Toggle visible menus
console.log('menusActive after: ', menusActive);
}, [menusActive]);
useEffect(() => {
let jsxArray = [];
let json2Array = [...menuJSON.MainMenu];
let menuSort = [];
menuSort['MainMenu'] = [];
for (const [key, element] of Object.entries(json2Array)) {
menuSort['MainMenu'].push(element);
if (element.submenu !== "none" && element.submenu !== undefined) {
let submenuKeys = Object.keys(element.submenu);
let subKey = submenuKeys[0];
menuSort[subKey] = [];
element.submenu[subKey].forEach(subelement => {
menuSort[subKey].push(subelement);
});
}
};
for (const [key, menus] of Object.entries(menuSort)) {
let itemArray = [];
for (const [k2, items] of Object.entries(menuSort[key])) {
function pcbValues(e) {
handleItemClick(items.submenukey);
}
itemArray.push(<MenuItem label={items.label} action={items.action} submenuKey={items.submenukey} handleClick={pcbValues} />)
}
jsxArray.push(<MenuList menuArray={itemArray} key={key} />);
}
setMenuListItemArray(jsxArray)
}, []);
return (
<div className='storybook-menudock'>
{menuListItemArray}
</div>
);
};
解决方案
问题是menusActive
处理函数在其范围内的数组只是初始渲染中的数组。看这里:
useEffect(() => {
let jsxArray = [];
// other code omitted
for (const [key, menus] of Object.entries(menuSort)) {
let itemArray = [];
for (const [k2, items] of Object.entries(menuSort[key])) {
function pcbValues(e) {
handleItemClick(items.submenukey);
}
itemArray.push(<MenuItem label={items.label} action={items.action} submenuKey={items.submenukey} handleClick={pcbValues} />)
}
jsxArray.push(<MenuList menuArray={itemArray} key={key} />);
}
setMenuListItemArray(jsxArray)
}, []);
有状态的menuListItemArray
变化只发生一次:在组件挂载之后,在效果挂钩中。在效果挂钩运行并填充状态后,它会永远保持这种状态。这意味着 a MenuItem
here 的这一部分也永远不会改变:
function pcbValues(e) {
handleItemClick(items.submenukey);
}
itemArray.push(<MenuItem label={items.label} action={items.action} submenuKey={items.submenukey} handleClick={pcbValues} />)
并且,在效果挂钩运行时,handleItemClick
在组件顶部声明的 引用初始数组 - 的["MainMenu"]
,仅此而已。稍后调用setMenusActive
不会改变这样一个事实,menusActive
即 JSX(在初始渲染后立即创建)具有范围的处理函数只是初始有状态数组。
这里的这段代码是让 JSX 进入状态很容易导致问题的原因之一。更好的方法是将(可序列化的)普通数组、对象和原语放入状态,然后在组件结束时返回时将状态转换为 JSX。像这样的东西:
export const MenuDock = ({ side, menuJSON }) => {
const [menuListObj, setMenuListObj] = useState([]);
// ...
useEffect(() => {
let jsxArray = [];
let json2Array = [...menuJSON.MainMenu];
let menuSort = [];
menuSort['MainMenu'] = [];
for (const [key, element] of Object.entries(json2Array)) {
menuSort['MainMenu'].push(element);
if (element.submenu !== "none" && element.submenu !== undefined) {
let submenuKeys = Object.keys(element.submenu);
let subKey = submenuKeys[0];
menuSort[subKey] = [];
element.submenu[subKey].forEach(subelement => {
menuSort[subKey].push(subelement);
});
}
};
setMenuListObj(menuSort);
}, []);
return (
<div className='storybook-menudock'>
{
Object.entries(menuListObj).map((key, menuItems) => <MenuList {...{ menuItems, key, handleItemClick }} />)
}
</div>
);
};
MenuList
组件在哪里获取menuItems, key, handleItemClick
道具(现在保证来自最近的更新!)并将它们变成 JSX 本身(替换以前的内容<MenuItem label={items.label} action={items.action} submenuKey={items.submenukey} handleClick={pcbValues} />
)
推荐阅读
- delphi - 如何在 Delphi 中读取 MP3 文件进行音频处理?
- objective-c - 将两位十六进制数转换为整数的最佳方法
- java - 使用 Junit 对边缘案例进行单元测试
- javascript - Fullcalendar TypeError:无法读取 null 的属性“顶部”
- mysql - Mysql:尽管文件内容包含数据,但最终字段中的空字段
- amazon-web-services - MongoDB BSON 文档在 Flask 应用程序上出现太大错误,但在独立执行时运行良好
- virtualbox - Vagrant up 返回错误
- python - Python/Pandas:仅当每个 Excel 文件包含某些值时才循环附加 Excel 文件
- c# - Xamarin.iOS 无法从引用的 System.Drawing.Common 中识别“位图”类型
- ios - Swift 中的“文件夹‘文档’不存在”错误