自定义反应下拉菜单无法正确打开关闭
Custom react dropdown menu not open close properly
目前,我在 table 中有 3 行。每行有两列:文件名和一个按钮
文件名只是一个虚拟的 link.
按钮将隐藏显示菜单。
我的要求如下:
- 点击一个按钮,它会切换菜单。即如果 prev 关闭,它应该打开。如果上一个打开,则关闭。
- 当你点击这个按钮时,如果其他菜单是打开的,它们应该是关闭的。
- 每次只打开1个菜单。
git clone, npm install, npm start
我有以下代码
import React, {useState, useEffect} from 'react';
function Menu({buttonName, menuIndex, currRowInd, setCurrRowInd}) {
// inside menu
const [open, setOpen] = useState(false);
const [showMenu, setShowMenu] = useState(false);
const menuItems = {download: 'download', view: 'view', delete: 'delete'};
useEffect(() => {
if (open && menuIndex === currRowInd) {
setShowMenu(true);
} else {
setShowMenu(false);
}
}, [open, currRowInd]);
return (
<div>
<button
onClick={event => {
// it is mouse click
if (event.pageX !== 0 && event.pageY !== 0) {
// toggle
setOpen(!open);
setCurrRowInd(menuIndex);
}
}}
>
{buttonName}
</button>
{showMenu && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
return (
<li
tabIndex="0"
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
>
{item}
</li>
);
})}
</ul>
)}
</div>
);
}
function TableElement() {
const [currRowInd, setCurrRowInd] = useState('');
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu
buttonName={item.button}
menuIndex={index}
currRowInd={currRowInd}
setCurrRowInd={setCurrRowInd}
/>
</td>
</tr>
);
})}
</tbody>
</table>
);
}
function App() {
return (
<>
<TableElement />
</>
);
}
export default App;
我有一个错误:
- 点击按钮 1,打开第一个菜单(好)
- 点击按钮2,打开第二个菜单并关闭第一个菜单(好)
- 再次点击按钮 1,第二个菜单关闭(目前还好),但是第一个菜单没有打开。
有什么想法吗?
您不需要菜单组件中的所有状态,它只会增加很多复杂性。您的问题只需将其删除并更改一些道具即可解决。
function Menu({buttonName, showMenu, handleClick}) {
const menuItems = {download: 'download', view: 'view', delete: 'delete'};
return (
<div>
<button
onClick={event => {
// it is mouse click
if (event.pageX !== 0 && event.pageY !== 0) {
// toggle
handleClick();
}
}}
>
{buttonName}
</button>
{showMenu && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
return (
<li
tabIndex="0"
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
>
{item}
</li>
);
})}
</ul>
)}
</div>
);
}
function TableElement() {
// number is a better default. -1 will never match
const [currRowInd, setCurrRowInd] = useState(-1);
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu
buttonName={item.button}
showMenu={index === currRowInd}
handleClick={() => {
// toggle
if (index !== currRowInd) setCurrRowInd(index)
else setCurrRowInd(-1)
}}
/>
</td>
</tr>
);
})}
</tbody>
</table>
);
}
如前所述,您可以进一步简化代码。由于每个 Table 只能显示一个菜单,因此应为 TableElement
组件处理可见性。
import React, { useState } from 'react';
const Menu = ({
buttonName,
menuIndex,
opened,
handleClickButton
}) => {
const menuItems = {
download: 'download',
view: 'view',
delete: 'delete'
};
return (
<div>
<button onClick={(e) => handleClickButton(menuIndex, e)}>
{buttonName}
</button>
{opened && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
return (
<li
tabIndex="0"
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
>
{item}
</li>
);
})}
</ul>
)}
</div>
);
}
const TableElement = _ => {
const [currRowInd, setCurrRowInd] = useState(null);
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
const handleButtonClick = (menuIndex, e) => {
setCurrRowInd(menuIndex);
}
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu
buttonName={item.button}
menuIndex={index}
opened={currRowInd === index}
handleClickButton={handleButtonClick}
/>
</td>
</tr>
);
})}
</tbody>
</table>
);
}
const App = _ => {
return (
<>
<TableElement />
</>
);
}
export default App;
目前,我在 table 中有 3 行。每行有两列:文件名和一个按钮
文件名只是一个虚拟的 link.
按钮将隐藏显示菜单。
我的要求如下:
- 点击一个按钮,它会切换菜单。即如果 prev 关闭,它应该打开。如果上一个打开,则关闭。
- 当你点击这个按钮时,如果其他菜单是打开的,它们应该是关闭的。
- 每次只打开1个菜单。
git clone, npm install, npm start
我有以下代码
import React, {useState, useEffect} from 'react';
function Menu({buttonName, menuIndex, currRowInd, setCurrRowInd}) {
// inside menu
const [open, setOpen] = useState(false);
const [showMenu, setShowMenu] = useState(false);
const menuItems = {download: 'download', view: 'view', delete: 'delete'};
useEffect(() => {
if (open && menuIndex === currRowInd) {
setShowMenu(true);
} else {
setShowMenu(false);
}
}, [open, currRowInd]);
return (
<div>
<button
onClick={event => {
// it is mouse click
if (event.pageX !== 0 && event.pageY !== 0) {
// toggle
setOpen(!open);
setCurrRowInd(menuIndex);
}
}}
>
{buttonName}
</button>
{showMenu && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
return (
<li
tabIndex="0"
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
>
{item}
</li>
);
})}
</ul>
)}
</div>
);
}
function TableElement() {
const [currRowInd, setCurrRowInd] = useState('');
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu
buttonName={item.button}
menuIndex={index}
currRowInd={currRowInd}
setCurrRowInd={setCurrRowInd}
/>
</td>
</tr>
);
})}
</tbody>
</table>
);
}
function App() {
return (
<>
<TableElement />
</>
);
}
export default App;
我有一个错误:
- 点击按钮 1,打开第一个菜单(好)
- 点击按钮2,打开第二个菜单并关闭第一个菜单(好)
- 再次点击按钮 1,第二个菜单关闭(目前还好),但是第一个菜单没有打开。
有什么想法吗?
您不需要菜单组件中的所有状态,它只会增加很多复杂性。您的问题只需将其删除并更改一些道具即可解决。
function Menu({buttonName, showMenu, handleClick}) {
const menuItems = {download: 'download', view: 'view', delete: 'delete'};
return (
<div>
<button
onClick={event => {
// it is mouse click
if (event.pageX !== 0 && event.pageY !== 0) {
// toggle
handleClick();
}
}}
>
{buttonName}
</button>
{showMenu && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
return (
<li
tabIndex="0"
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
>
{item}
</li>
);
})}
</ul>
)}
</div>
);
}
function TableElement() {
// number is a better default. -1 will never match
const [currRowInd, setCurrRowInd] = useState(-1);
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu
buttonName={item.button}
showMenu={index === currRowInd}
handleClick={() => {
// toggle
if (index !== currRowInd) setCurrRowInd(index)
else setCurrRowInd(-1)
}}
/>
</td>
</tr>
);
})}
</tbody>
</table>
);
}
如前所述,您可以进一步简化代码。由于每个 Table 只能显示一个菜单,因此应为 TableElement
组件处理可见性。
import React, { useState } from 'react';
const Menu = ({
buttonName,
menuIndex,
opened,
handleClickButton
}) => {
const menuItems = {
download: 'download',
view: 'view',
delete: 'delete'
};
return (
<div>
<button onClick={(e) => handleClickButton(menuIndex, e)}>
{buttonName}
</button>
{opened && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
return (
<li
tabIndex="0"
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
>
{item}
</li>
);
})}
</ul>
)}
</div>
);
}
const TableElement = _ => {
const [currRowInd, setCurrRowInd] = useState(null);
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
const handleButtonClick = (menuIndex, e) => {
setCurrRowInd(menuIndex);
}
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu
buttonName={item.button}
menuIndex={index}
opened={currRowInd === index}
handleClickButton={handleButtonClick}
/>
</td>
</tr>
);
})}
</tbody>
</table>
);
}
const App = _ => {
return (
<>
<TableElement />
</>
);
}
export default App;