输入文本在每个字符输入后都会失去焦点,但这似乎是React中的一个目标问题



我正在使用Material UI table创建一个表。我想通过从api获取来填充,这似乎很好。现在,每行应该显示两个按钮,其中一个用于使用filter((方法删除该行。另一个是修改标题。

为了删除行,我将针对按钮的父ID(TableRow ID(,并筛选索引与TableRow标识不匹配的数组中的所有元素,这似乎很好,唯一的问题是我必须使用id - 1,使其与索引号匹配。这似乎很有效。

对于标题的编辑,我遇到了一些问题。这是一个循序渐进的过程:

  1. 编辑按钮切换输入中的禁用值,使其变为"可编辑"。handleClickEdit函数设置数据的新状态,以便启用输入。

  2. 输入的onChange运行handleChange()函数,这将创建一个新的数组,并基于id - 1(类似于remove函数(作为索引,设置该对象的title属性,然后将该新数组设置为data的新状态值**这里的一个问题是,在任何字符输入中,输入字段都是非焦点的,您必须再次单击才能继续输入。请注意,如果我使用id而不是id - 1,则不会发生这种情况,但它会更改下面的字段。**

  3. 该值存储在状态中,当单击"完成"按钮时,handleDone功能会再次将输入字段切换为禁用状态。

  4. 这种方法的一个额外且令人讨厌的问题是,如果删除一行,然后想要编辑被删除行下面的行,则编辑的目标是下面的行而不是实际的行。

这是我的代码:

import React, { useEffect, useState } from "react";
import "./App.css";
import {
TablePagination,
Table,
TableBody,
TableFooter,
TableContainer,
TableRow,
TableHead,
} from "@mui/material";
import TableCell from "@mui/material/TableCell";
function App() {
const [data, setData] = useState([]);
const [toggleEdit, setToggleEdit] = useState(false);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/photos?_limit=5")
.then((res) => res.json())
.then((data) => {
setData(data.map((item) => ({ ...item, enableEdit: true })));
});
}, []);
const removeRow = (e) => {
const { id } = e.target.parentElement;
let arr = data.filter((elem) => elem.id != id);
setData(arr);
};
const handleClickEdit = (e) => {
const { id } = e.target.parentElement;
setToggleEdit(!toggleEdit);
let newData = [...data];
newData[id-1].enableEdit = !newData[id-1].enableEdit;
setData(newData);
};
const handleChange = (e) => {
const { value, id } = e.target;
let newData = [...data];
newData[id-1].title = value;
setData(newData);
};
const handleDone = (e) => {
const { id } = e.target.parentElement;
let newData = [...data];
newData[id-1].enableEdit = !newData[id-1].enableEdit;
setData(newData);
console.log(data[id-1]);
};
return (
<div>
<div className="table-container">
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Album ID</TableCell>
<TableCell>Title</TableCell>
{/* <TableCell>Thumbnail</TableCell> */}
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((row) => (
<TableRow id={row.id} key={row.id}>
<TableCell key={row.id + "_id"} id={row.id + "_id"}>
{row.id}
</TableCell>
<TableCell>{row.albumId}</TableCell>
<TableCell key={row.title} id={row.title}>
<input
type="text"
disabled={row.enableEdit}
className="title"
defaultValue={row.title}
id={row.id}
onChange={(e) => handleChange(e)}
name={"title"}
/>
</TableCell>
{/* <TableCell><img src={row.thumbnailUrl} /></TableCell> */}
<TableCell id={row.id}>
<button onClick={(e) => removeRow(e)}>Remove</button>
{row.enableEdit && (
<button onClick={(e) => handleClickEdit(e)}>Edit</button>
)}
{row.enableEdit === false && (
<button onClick={(e) => handleDone(e)}>Done</button>
)}
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
</TableFooter>
</Table>
</TableContainer>
</div>
</div>
);
}
export default App;

我会通过创建一个自定义的Row组件来管理每一行的状态。这样就不会重新渲染每一行onChange

很抱歉我还没有测试过这个代码,但它应该能让你大致了解如何进行

行组件:

const MyRow = ({ row, handleSave, handleRemove }) => {
const [disabled, setDisabled] = useState(true);
const [value, setValue] = useState(row.title);
return (
<>
<TableRow>
<TableCell>{row.id}</TableCell>
<TableCell>{row.albumId}</TableCell>
<TableCell key={row.title} id={row.title}>
<input
type="text"
value={value}
disabled={disabled}
className="title"
defaultValue={row.title}
onChange={(e) => setValue(e.target.value)}
name={"title"}
/>
</TableCell>
<TableCell>
<button
onClick={() => {
if (disabled) {
setDisabled(false);
} else {
//   save changes
handleSave(row.id, value);
// and disable editing
setDisabled(true);
}
}}
>
{disabled ? "Edit" : "Save"}
</button>
{/* you didn't inclucde this in your code, but a 'cancel' button is probably handy */}
{!disabled && (
<button
onClick={() => {
setValue(row.title);
setDisabled(true);
}}
>
Cancel
</button>
)}
</TableCell>
<TableCell>
<button onClick={() => handleRemove(row.id)}>Remove</button>
</TableCell>
</TableRow>
</>
);
};

请注意,您不必在这里对元素id道具大惊小怪。一般来说,我很少发现需要在React中使用元素ID。神奇之处在于将行数据本身传递到事件处理程序回调中。

在主组件中定义data状态处理程序,并将它们向下传递到每一行。同样,不要对ids大惊小怪,只需确保每一行都有一个唯一的key


function App() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/photos?_limit=5")
.then((res) => res.json())
.then((data) => {
setData(data.map((item) => ({ ...item, enableEdit: true })));
});
}, []);
const handleSave = (id, value) => {
// generate a new array from existin grows
const newData = data.map((row) => {
// update our row with the new value
if (row.id === id) {
return { ...row, title: value };
}
// not our row—leave it as-is
return row;
});
// update our parent data state
setData(newData);
};
const handleRemove = (id) => {
let newData = data.filter((row) => row.id !== id);
setData(newData);
};
return (
<div>
<div className="table-container">
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Album ID</TableCell>
<TableCell>Title</TableCell>
{/* <TableCell>Thumbnail</TableCell> */}
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((row) => (
<MyRow
key={row.id}
row={row}
handleSave={handleSave}
handleRemove={handleRemove}
/>
))}
</TableBody>
<TableFooter></TableFooter>
</Table>
</TableContainer>
</div>
</div>
);
}
export default App;

最新更新