Как преобразовать в onClick в React

#javascript #reactjs

#javascript #reactjs

Вопрос:

Я хочу преобразовать <td>MyData</td> в <input value="MyData" /> , когда я нажимаю на <td>Edit</td> . Когда я нажимаю на <td>Edit</td> , у меня появляется идентификатор полной строки. На самом деле я хочу добавить функцию редактирования в свой код. Всякий раз, когда я нажимаю на edit, это должно изменить все <td>MyData</td> поле ввода into, где я могу его изменить. Ниже приведен мой код.

 function ManagerGrid(props)
{
    function handleClick(value)
    {
        console.log(value)
    }
    const listItems = props.tasks.map((task)=>
    {
        return(
            <tr key={task._id}>
                <td>{task._id}</td>
                <td>{task.title}</td>
                <td>{task.detail}</td>
                <td>{task.assignee}</td>
                <td>{task.time}</td>
                <td onClick={(e)=>{handleClick(task._id)}}>Edit</td>
            </tr>
        )
    })


    return(
        <table id="myTable">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Title</th>
                    <th>Detail</th>
                    <th>Assignee</th>
                    <th>Time</th>
                </tr>
            </thead>
            <tbody>
                {listItems}
            </tbody>
        </table>
    )
}

  

Ответ №1:

Ваш код рендеринга должен изменяться на основе его текущего состояния, то есть редактироваться или не редактироваться. Вам нужно состояние isEditing. Вот пример, вам нужно будет разобраться, как сохранить изменения. Это основано на перехватах, но вы также можете передать его со своими реквизитами и отслеживать его в родительском компоненте.

 function ManagerGrid(props)
{
    const [editingId, setEditingId] = useState(-1)
    function handleClick(value)
    {
        setEditingId(value)
        // TODO: cancel or save edits
    }
    const listItems = props.tasks.map((task)=>
    {
        if (editingId === task._id) {
        return(
            <tr key={task._id}>
                <td><input value={task._id} /></td>
                <td><input value={task.title} /></td>
                <td><input value={task.detail} /></td>
                <td><input value={task.assignee} /></td>
                <td><input value={task.time} /></td>
                <td onClick={(e)=>{handleSave(/*TODO*/)}}>Save</td>
            </tr>)
         else {
            return (<tr key={task._id}>
                <td>{task._id}</td>
                <td>{task.title}</td>
                <td>{task.detail}</td>
                <td>{task.assignee}</td>
                <td>{task.time}</td>
                <td onClick={(e)=>{handleClick(task._id)}}>Edit</td>
            </tr>)
         }
    })


    return(
        <table id="myTable">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Title</th>
                    <th>Detail</th>
                    <th>Assignee</th>
                    <th>Time</th>
                </tr>
            </thead>
            <tbody>
                {listItems}
            </tbody>
        </table>
    )
}
  

Ответ №2:

Вы можете достичь этого, создав пользовательский компонент, подобный этому:

 function ManagerGrid(props) {
  const [editedRowId, setEditedRowId] = useState(null);

  function handleClick(value) {
    setEditedRowId(value);
  }

  const listItems = props.tasks.map((task) => {
    const CellContent = ({ children }) => {
      const isCurrentTaskEdit = task._id === editedRowId;

      return (
        <td>
          {isCurrentTaskEdit ? (
            <input style={{ width: "50px" }} value={children} />
          ) : (
            children
          )}
        </td>
      );
    };

    return (
      <tr key={task._id}>
        <CellContent>{task._id}</CellContent>
        <CellContent>{task.title}</CellContent>
        <CellContent>{task.detail}</CellContent>
        <CellContent>{task.assignee}</CellContent>
        <CellContent>{task.time}</CellContent>
        <td
          onClick={(e) => {
            handleClick(task._id);
          }}
        >
          Edit
        </td>
      </tr>
    );
  });
  

Ответ №3:

Такого рода проблемы обычно можно решить путем создания компонентов меньшего размера:

 const ReadListItem = ({
  task,
  onEdit
}) => {
  return (
    <tr>
      <td>{task._id}</td>
      <td>{task.title}</td>
      <td>{task.detail}</td>
      <td>{task.assignee}</td>
      <td>{task.time}</td>
      <td onClick={onEdit}>Edit</td>
    </tr>
  );
};

const EditListItem = ({
  task,
  onDone
}) => {
  const [title, setTitle] = useState(task.title);
  const [detail, setDetail] = useState(task.detail);
  const [assignee, setAssignee] = useState(task.assignee);
  const [time, setTime] = useState(task.time);

  const finished = useCallback(() => {
    onDone({
      _id: task._id,
      title,
      detail,
      assignee,
      time
    });
  }, [task, title, detail, assignee, time]);

  useEffect(() => {
    setTitle(task.title);
    setDetail(task.detail);
    setAssignee(task.assignee);
    setTime(task.time);
  }, [task]);

  return (
    <tr>
      <td>{task._id}</td>
      <td><input type='text' value={title} onChange={e => setTitle(e.target.value)} /></td>
      <td><input type='text' value={detail} onChange={e => setDetail(e.target.value)} /></td>
      <td><input type='text' value={assignee} onChange={e => setAssignee(e.target.value)} /></td>
      <td><input type='text' value={time} onChange={e => setTime(e.target.value)} /></td>
      <td onClick={finished}>Done</td>
    </tr>
  );
}

const ListItem = ({
  task,
  onEdited
}) => {
  const [editing, setEditing] = useState(false);

  return editing
    ? <EditListItem task={task} onDone={(item) => onEdited(item)}>
    : <ReadListItem task={task} onEdit={() => setEditing(true)} />
}
  

У вас мог бы быть другой компонент для входных данных, чтобы уменьшить объем кода, и вы могли бы лучше использовать useCallback для обработчиков onChange.

Использование было бы намного проще:

 const ManagerGrid = ({
  tasks
}) => {
  const onEdited = useCallback((task) => {
    console.log({task});
  }, []);

  return (
    <table id="myTable">
      <thead>
        <tr>
          <th>ID</th>
          <th>Title</th>
          <th>Detail</th>
          <th>Assignee</th>
          <th>Time</th>
        </tr>
      </thead>
      <tbody>
        { tasks.map(task => <ListItem key={task._id} task={task} onEdited={onEdited} />) }
      </tbody>
    </table>
  );
}
  

Вы должны отметить, что я сделал так, что задачи нигде не изменяются, потому что это может исходить из источника данных, который считается неизменяемым. Однако это можно обработать в окончательном обратном вызове onEdited, где вы могли бы либо изменить список задач, либо отправить действие для его обработки.

Эффект использования внутри EditListItem возникает в случае, если задача изменяется извне компонента.

Я думаю, что это охватывает большинство вопросов, которые вы задавали, но, пожалуйста, добавляйте комментарии, если я что-то пропустил.

Комментарии:

1. Я на самом деле не запускал это, поэтому это может быть не на 100% корректно, и также может быть возможно объединить несколько состояний использования в одно состояние или пользовательский редактор

2. Я также намеренно не сопоставил _id с редактируемым полем, потому что на самом деле вы не должны редактировать идентификаторы