onClick 事件不适用于悬停时显示的图标

问题描述 投票:0回答:1

在这里反应新手。

有一个 ItemsList 组件,它基本上是一个显示一些信息的表格。当表中的行悬停时,我设法显示两个图标(编辑和删除)(与 Gmail 相同)。然后我想渲染一个 DeleteModal 组件,这基本上是单击删除图标时的删除确认。

The table and the icons on hover

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 事件时出现问题。

提前谢谢您。

reactjs react-redux react-dom
1个回答
0
投票

您尝试做的事情很奇怪,而且不是您处理此问题的方式。你永远不应该在组件内调用

ReactDOMServer.renderToString
- 这将创建一个孤立的“组件”,它只是一个静态字符串,它将无法通过标准 React 构造与树的其余部分进行交互。

此外,设置 React 拥有的任何内容的

innerHTML
通常是不允许的。由于它是 React 拥有的 DOM,并且这是更改该 DOM 的命令式调用,因此它实际上是在 React 构造之外进行变异,这将不起作用,因为这些变异将在重新渲染时被擦除。

不幸的是,您在 SO 上找到的答案是非常糟糕的建议。

这几乎肯定是问题所在。所以我们需要重构悬停行为。

两种选择:

  1. 做到这样
    onMouseEnter
    ,行的ID存储在某种状态中,然后在行的渲染中,对于与该ID匹配的行——我们渲染图标。
  2. 始终将每一行的按钮渲染到 DOM 中,但使用 CSS 在悬停时显示/隐藏它们。

选项 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>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.