在这里反应新手。
有一个 ItemsList 组件,它基本上是一个显示一些信息的表格。当表中的行悬停时,我设法显示两个图标(编辑和删除)(与 Gmail 相同)。然后我想渲染一个 DeleteModal 组件,这基本上是单击删除图标时的删除确认。
import styles from './ItemsList.module.scss';
import '../../../styles/buttons.scss';
const ItemsList = () => {
const dispatch = useDispatch();
const handleOpenDeleteItemModal = () => {
dispatch(SET_DELETE_ITEM_MODAL(true));
dispatch(SET_SIDEBAR(false));
};
const handleMouseEnter = (e) => {
e.currentTarget.lastElementChild.innerHTML = ReactDOMServer.renderToString(
<>
<BiMessageSquareEdit className={styles.list__icons} />
<BsTrash
className={styles.list__icons}
onClick={handleOpenDeleteItemModal}
/>
</>
);
};
const handleMouseLeave = (e, createdAt) => {
e.currentTarget.lastElementChild.innerHTML = ReactDOMServer.renderToString(
<Moment format="DD/MM/YY">{createdAt}</Moment>
);
};
return (
<div className={styles.list__table}>
{!isLoading && items.length === 0 ? (
<p>No items found, please add an item.</p>
) : (
<table>
<thead>
<tr>
<th>Number</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Quantity</th>
<th>Value</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{currentItems.map((item, index) => {
const { _id, name, category, price, quantity, createdAt } =
item;
return (
<tr
onMouseEnter={(e) => handleMouseEnter(e)}
onMouseLeave={(e) => handleMouseLeave(e, createdAt)}
key={_id}
>
<td>{index + 1 + '.'}</td>
<td>{shortenText(name, 15)}</td>
<td>{category}</td>
<td>
{'£'}
{price}
</td>
<td>{quantity}</td>
<td>
{'£'}
{price * quantity}
</td>
<td>
<Moment format="DD/MM/YY">{createdAt}</Moment>
</td>
</tr>
);
})}
</tbody>
</table>
)}
</div>)
}
CSS
.list__container {
padding: 2rem;
hr {
border: 1px solid a.$hr;
}
.list__topSection {
padding: 3rem 0 1.5rem 0;
display: flex;
align-items: center;
justify-content: space-between;
h3 {
font-size: 1.5rem;
font-family: a.$roboto;
font-weight: a.$medium;
}
}
.list__table {
font-family: a.$roboto;
table {
border-collapse: collapse;
width: 100%;
font-size: 1rem;
}
th,
td {
vertical-align: top;
text-align: left;
padding: 8px;
}
th {
font-weight: a.$regular;
background-color: a.$primary-color;
color: white;
}
tr {
border-bottom: 1px solid #ccc;
}
tr:nth-child(odd) {
background-color: rgb(234, 234, 234);
}
tr:nth-child(even) {
background-color: #fff;
}
}
.pagination {
list-style: none;
display: flex;
justify-content: center;
align-items: center;
margin: 2rem 0;
font-size: 1rem;
position: absolute;
bottom: 0;
right: 0;
left: 0;
}
.pagination,
.page__num,
.page__next__prev {
font-family: a.$roboto;
padding: 5px 10px;
cursor: pointer;
border-radius: 3px;
margin: 2px;
}
.pagination .page__num {
border: 1px solid a.$primary-color;
}
.page__active {
background-color: a.$primary-color;
color: rgb(255, 255, 255);
height: 100%;
}
.pagination .page__num:hover {
color: #fff;
background-color: a.$primary-color;
}
.page__disabled__link {
color: rgb(182, 182, 182);
cursor: none;
}
}
.list__icons {
font-size: 1.2rem;
cursor: pointer;
}
当我手动将模态状态更改为 true 时,它会正常渲染。所以我假设显示图标或 onClick 事件时出现问题。
提前谢谢您。
您尝试做的事情很奇怪,而且不是您处理此问题的方式。你永远不应该在组件内调用
ReactDOMServer.renderToString
- 这将创建一个孤立的“组件”,它只是一个静态字符串,它将无法通过标准 React 构造与树的其余部分进行交互。
此外,设置 React 拥有的任何内容的
innerHTML
通常是不允许的。由于它是 React 拥有的 DOM,并且这是更改该 DOM 的命令式调用,因此它实际上是在 React 构造之外进行变异,这将不起作用,因为这些变异将在重新渲染时被擦除。
不幸的是,您在 SO 上找到的答案是非常糟糕的建议。
这几乎肯定是问题所在。所以我们需要重构悬停行为。
两种选择:
onMouseEnter
,行的ID存储在某种状态中,然后在行的渲染中,对于与该ID匹配的行——我们渲染图标。选项 2 几乎肯定更好,因为当 CSS 知道状态时,您不需要管理不必要的状态。此外,当您在行中上下移动时,它会感觉性能更高,因为当用户移动鼠标时不会发生重新渲染。
更改CSS:
.list__container {
padding: 2rem;
hr {
border: 1px solid a.$hr;
}
.list__topSection {
padding: 3rem 0 1.5rem 0;
display: flex;
align-items: center;
justify-content: space-between;
h3 {
font-size: 1.5rem;
font-family: a.$roboto;
font-weight: a.$medium;
}
}
.list__table {
font-family: a.$roboto;
table {
border-collapse: collapse;
width: 100%;
font-size: 1rem;
}
th,
td {
vertical-align: top;
text-align: left;
padding: 8px;
}
th {
font-weight: a.$regular;
background-color: a.$primary-color;
color: white;
}
tr {
border-bottom: 1px solid #ccc;
}
tr:nth-child(odd) {
background-color: rgb(234, 234, 234);
}
tr:nth-child(even) {
background-color: #fff;
}
tr:hover {
.list__icons {
display: inline-block;
}
.list__date {
display: none;
}
}
}
.pagination {
list-style: none;
display: flex;
justify-content: center;
align-items: center;
margin: 2rem 0;
font-size: 1rem;
position: absolute;
bottom: 0;
right: 0;
left: 0;
}
.pagination,
.page__num,
.page__next__prev {
font-family: a.$roboto;
padding: 5px 10px;
cursor: pointer;
border-radius: 3px;
margin: 2px;
}
.pagination .page__num {
border: 1px solid a.$primary-color;
}
.page__active {
background-color: a.$primary-color;
color: rgb(255, 255, 255);
height: 100%;
}
.pagination .page__num:hover {
color: #fff;
background-color: a.$primary-color;
}
.page__disabled__link {
color: rgb(182, 182, 182);
cursor: none;
}
}
.list__icons {
display: none;
font-size: 1.2rem;
cursor: pointer;
}
现在渲染全部并应用正确的类名称:
const ItemsList = () => {
const dispatch = useDispatch();
const handleOpenDeleteItemModal = () => {
dispatch(SET_DELETE_ITEM_MODAL(true));
dispatch(SET_SIDEBAR(false));
};
return (
<div className={styles.list__table}>
{!isLoading && items.length === 0 ? (
<p>No items found, please add an item.</p>
) : (
<table>
<thead>
<tr>
<th>Number</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Quantity</th>
<th>Value</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{currentItems.map((item, index) => {
const { _id, name, category, price, quantity, createdAt } = item;
return (
<tr key={_id}>
<td>{index + 1 + "."}</td>
<td>{shortenText(name, 15)}</td>
<td>{category}</td>
<td>
{"£"}
{price}
</td>
<td>{quantity}</td>
<td>
{"£"}
{price * quantity}
</td>
<td>
<Moment format="DD/MM/YY" className={styles.list__date}>{createdAt}</Moment>
<>
<BiMessageSquareEdit className={styles.list__icons} />
<BsTrash
className={styles.list__icons}
onClick={handleOpenDeleteItemModal}
/>
</>
</td>
</tr>
);
})}
</tbody>
</table>
)}
</div>
);
};