首页 > 解决方案 > React - map 函数仅使用数组中的最后一项设置输入值

问题描述

我有这个带有输入字段的 RenamePopover 组件,我在位于 Board 组件中的 map 函数中使用它。当我遍历数组时,我将“board.name”作为 value 属性传递给 RenamePopover,这样最后,每个渲染的元素都会有自己的弹出框,其输入字段预先填充了名称。不幸的是,情况并非如此,因为在渲染后每个输入字段都设置为数组中最后一个元素的名称。我错过了什么?

Board.js

import React from 'react'
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getActiveBoard } from '../../state/action-creators/activeBoardActions';

import RenamePopover from '../popovers/RenamePopover';

const Board = () => {
  const dispatch = useDispatch();
  const [anchorEl, setAnchorEl] = React.useState(null);
  const boards = useSelector((state) => state.board.items);
  const open = Boolean(anchorEl);

  useEffect(() => {
    dispatch(getActiveBoard());
  }, [boards])

  const handleClick = (id) => {
    const anchor = document.getElementById(`board-anchor${id}`);
    setAnchorEl(anchor);
  };
  
  const handleClose = () => {
    setAnchorEl(null);
  };
    

  const single_board = boards && boards.map((board) => {
    return (
      <li key={board.id} className={`row hovered-nav-item board-item ${active_board == board.id ? "item-selected" : ""}`}>
        <span className="d-flex justify-content-between">
          <div onClick={() => onBoardClick(board.id)} className="fs-5 text-white board-name" id={`board-anchor${board.id}`}>
            {board.name}
          </div>

          <svg onClick={() => handleClick(board.id)} xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
            className="bi bi-pen-fill rename-add-icon rename-add-icon-sidebar"
            viewBox="0 0 16 16">
            <path
              d="m13.498.795.149-.149a1.207 1.207 0 1 1 1.707 1.708l-.149.148a1.5 1.5 0 0 1-.059 2.059L4.854 14.854a.5.5 0 0 1-.233.131l-4 1a.5.5 0 0 1-.606-.606l1-4a.5.5 0 0 1 .131-.232l9.642-9.642a.5.5 0 0 0-.642.056L6.854 4.854a.5.5 0 1 1-.708-.708L9.44.854A1.5 1.5 0 0 1 11.5.796a1.5 1.5 0 0 1 1.998-.001z" />
          </svg>
          <RenamePopover
            open={open}
            anchorEl={anchorEl} 
            onClose={handleClose}
            placeholder="Enter new name"
            value={board.name}
          />
        </span>
      </li>
    )
  })

  return(
    <div className="container" id="sidebar-boards">
      {single_board}
    </div>
  ) 
  
}

export default Board

重命名Popover.js

import { Popover } from '@material-ui/core'
import React from 'react'

const RenamePopover = (props) => {
  const [value, setValue] = React.useState(props.value);

  const handleChange = (e) => {
    setValue(e.target.value);
  }
  
  return (
    <Popover
      open={props.open}
      anchorEl={props.anchorEl}
      onClose={props.onClose}
      anchorOrigin={{
        vertical: 'center',
        horizontal: 'right',
      }}
      transformOrigin={{
        vertical: 'center',
        horizontal: 'left',
      }}
    >
      <input autoFocus={true} className="card bg-dark text-light add-input" type="text" 
      placeholder={props.placeholder} 
      value={value} 
      onChange={handleChange}
      />
    </Popover>
  )
}

export default RenamePopove

r

标签: javascriptreactjs

解决方案


问题

这个问题是您正在为弹出框使用单个open“状态”。您将 设置为anchorEl正在与之交互的元素,然后打开所有弹出框。

解决方案 1

添加open状态以跟踪您要打开的特定板元素。

const Board = () => {
  ...

  const [anchorEl, setAnchorEl] = React.useState(null);
  const [open, setOpen] = React.useState(null); // <-- add state to hold id

  ...

  const handleClick = (id) => {
    const anchor = document.getElementById(`board-anchor${id}`);
    setAnchorEl(anchor);
    setOpen(id);
  };

  const handleClose = () => {
    setAnchorEl(null);
    setOpen(null);
  };

  const single_board =
    boards &&
    boards.map((board) => {
      return (
        <li
          key={board.id}
          className={`row hovered-nav-item board-item ${active_board == board.id ? "item-selected" : ""}`}
        >
          <span className="d-flex justify-content-between">
            <div
              onClick={() => handleClick(board.id)}
              className="fs-5 text-white board-name"
              id={`board-anchor${board.id}`}
            >
              {board.name}
            </div>
            ...
            <RenamePopover
              open={open === board.id} // <-- match board id
              anchorEl={anchorEl}
              onClose={handleClose}
              placeholder="Enter new name"
              value={board.name}
            />
          </span>
        </li>
      );
    });

...

解决方案 2

使用open状态来保存board您想要打开/渲染的全部数据并渲染单个弹出框。您需要向useEffectpopover 组件添加一个钩子来处理重置本地状态。

const RenamePopover = (props) => {
  const [value, setValue] = React.useState(props.value);

  React.useEffect(() => {
    setValue(props.value);
  }, [props.value]);

  const handleChange = (e) => {
    setValue(e.target.value);
  };

  return (
    <Popover
      open={props.open}
      anchorEl={props.anchorEl}
      onClose={props.onClose}
      anchorOrigin={{
        vertical: "center",
        horizontal: "right"
      }}
      transformOrigin={{
        vertical: "center",
        horizontal: "left"
      }}
    >
      <input
        autoFocus={true}
        className="card bg-dark text-light add-input"
        type="text"
        placeholder={props.placeholder}
        value={value}
        onChange={handleChange}
      />
    </Popover>
  );
};
const Board = () => {
  ...

  const [anchorEl, setAnchorEl] = React.useState(null);
  const [open, setOpen] = React.useState(null);

  ...

  const handleClick = (board) => {
    const anchor = document.getElementById(`board-anchor${board.id}`);
    setAnchorEl(anchor);
    setOpen(board);
  };

  const handleClose = () => {
    setAnchorEl(null);
    setOpen(null);
  };

  const single_board =
    boards &&
    boards.map((board) => {
      return (
        <li
          key={board.id}
          className={`row hovered-nav-item board-item ${active_board == board.id ? "item-selected" : ""}`}
        >
          <span className="d-flex justify-content-between">
            <div
              onClick={() => handleClick(board)}
              className="fs-5 text-white board-name"
              id={`board-anchor${board.id}`}
            >
              {board.name}
            </div>
            ...
          </span>
        </li>
      );
    });

  return (
    <div className="container" id="sidebar-boards">
      {single_board}
      <RenamePopover // <-- render 1 popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        placeholder="Enter new name"
        value={open?.name}
      />
    </div>
  );
};

推荐阅读