我正在尝试针对特定行在 React 中的表上进行编辑。
我的代码看起来像这样......
const [rowData, setRowData] = useState({ kind: { str: '', row: '' }});
const onChange = e => {
setRowData({...rowData, [e.target.name]: e.target.value }}
}
arr.map((ele, index) => (
<tr>
<td><input type='text' name='kind' value={kind.str} row={index} onChange={e => onChange(e)}></td>
</tr>
))
我没有编写所有代码,但我认为这足以回答。基本上我按下一个按钮edit,使行可编辑。当我按下这个按钮时,实际上所有的行和列都变成了可编辑的,这不是我想要的,但不管这个结果如何——我去更新表格,只有一个输入得到更新,不幸的是我得到了这个错误
A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
阅读错误后,我决定将我的onChange功能更改为...
const onChange = e => {
setRowData({...rowData, [e.target.name]: { str: e.target.value, row: e.target.getAttribute('row')}}
}
以上解决了使我的controlled组件成为uncontrolled但是每行上与kind名称匹配的所有输入字段都得到更新的问题,这不是我想要的功能。
我该如何解决这个问题。最终我想要的是一个可编辑的表,用户可以更新一行并使用他们输入的数据更新数据库。
arr我使用的变量实际上是命名files的。
我有另一个组件,它从我的数据库中获取我的所有文件并将其设置为 files 变量
const [files, setFiles] = useState([]);
useEffect(() => {
const fetchFiles = async () => {
setLoading(true);
const res = await fetch('http://localhost:5000/api/files/all', {
method: 'GET',
});
const data = await res.json();
setFiles(data);
setLoading(false);
};
fetchFiles();
}, []);
然后,我将作为道具文件传递给我的 Items 组件,这是我为我的问题编写的代码所在的位置。
大部分代码都在这里...
ShowList 组件
const ShowList = () => {
const [files, setFiles] = useState([]);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [filesPerPage, setFilesPerPage] = useState(5);
const [yourUploads, setYourUploads] = useState(false);
useEffect(() => {
const fetchFiles = async () => {
setLoading(true);
const res = await fetch('http://localhost:5000/api/files/all', {
method: 'GET',
});
const data = await res.json();
setFiles(data);
setLoading(false);
};
fetchFiles();
}, []);
const indexOfLastPage = currentPage * filesPerPage;
const indexOfFirstPage = indexOfLastPage - filesPerPage;
const currentFiles = files.slice(indexOfFirstPage, indexOfLastPage);
const paginate = (pageNumber) => setCurrentPage(pageNumber);
return (
<Fragment>
<div className='container'>
<h3 className='text-center'>
{yourUploads ? 'Your uploads' : 'All uploads'}
</h3>
<div className='d-flex dropdown'>
<button
className='btn mb-3 mr-3'
type='button'
id='dropdownMenuButton'
data-toggle='dropdown'
aria-haspopup='true'
aria-expanded='false'
>
Pages per row {filesPerPage}
</button>
<div className='dropdown-menu' aria-labelledby='dropdownMenuButton'>
<button
className='dropdown-item'
onClick={() => setFilesPerPage(5)}
>
5
</button>
<button
className='dropdown-item'
onClick={() => setFilesPerPage(10)}
>
10
</button>
</div>
<div className='dropdown'>
<button
className='btn mb-3'
type='button'
id='dropdownUploadsButton'
data-toggle='dropdown'
aria-haspopup='true'
aria-expanded='false'
>
{yourUploads ? 'your uploads' : 'all uploads'}
</button>
<div
className='dropdown-menu'
aria-labelledby='dropdownUploadButton'
>
<button
className='dropdown-item'
onClick={() => setYourUploads(true)}
>
your uploads
</button>
<button
className='dropdown-item'
onClick={() => setYourUploads(false)}
>
all uploads
</button>
</div>
</div>
</div>
<table id='myTable' className='table table-striped w-100'>
<thead>
<tr>
<th scope='col'>
<small>Title</small>
</th>
<th scope='col'>
<small>Kind</small>
</th>
<th scope='col'>
<small>Size</small>
</th>
<th scope='col'>
<small>Strength</small>
</th>
<th scope='col'>
<small>Combinations</small>
</th>
<th scope='col'>
<small>Favors</small>
</th>
<th scope='col'>
<small>Stock</small>
</th>
<th scope='col'>
<small>Carousel</small>
</th>
<th scope='col'>
<small>Owner</small>
</th>
<th scope='col'>
<small>Edit</small>
</th>
<th scope='col'>
<small>Delete</small>
</th>
</tr>
</thead>
<Items
files={currentFiles}
loading={loading}
yourUploads={yourUploads}
/>
</table>
<Pagination
filesPerPage={filesPerPage}
totalFiles={files.length}
paginate={paginate}
/>
</div>
</Fragment>
);
};
物品组件
const Items = ({ files, loading, yourUploads }) => {
const [myUploads, setMyUploads] = useState([]);
const [editable, setEditable] = useState(false);
const [rowData, setRowData] = useState({
kind: { str: '', row: '' },
});
const { kind } = rowData;
useEffect(() => {
const fetchMyUploads = async () => {
const res = await fetch('http://localhost:5000/api/files/all/mine', {
method: 'GET',
});
const data = await res.json();
setMyUploads(data);
};
fetchMyUploads();
}, [files]);
const onChange = (e) => {
setRowData({ ...rowData, [e.target.name]: e.target.value });
};
const onSubmit = async (e, file_id) => {
e.preventDefault();
if (!editable) {
setEditable(!editable);
} else {
await fetch(`http://localhost:5000/api/files/${file_id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(rowData),
});
setEditable(!editable);
}
};
if (loading) {
return (
<tbody>
<tr>
<td>loading...</td>
</tr>
</tbody>
);
}
const list = yourUploads
? myUploads.map((file) => (
<tr key={file._id}>
<td>
<small>{file.title}</small>
</td>
<td>
<small>{file.kind}</small>
</td>
<td>
<small>{file.size}</small>
</td>
<td>
<small>{file.strength}</small>
</td>
<td>
<small>{file.combinations}</small>
</td>
<td>
<small>{file.favors}</small>
</td>
<td>
<small
className={file.availability ? 'alert-success' : 'alert-danger'}
>
{file.availability ? 'in stock' : 'out of stock'}
</small>
</td>
<td>
<small>{file.isCarousel ? 'carousel' : 'not caorousel'}</small>
</td>
<td>
<small>{file.owner}</small>
</td>
<td>
<button className='btn btn-dark'>
<small>edit</small>
</button>
</td>
<td>
<button className='btn btn-danger'>
<small>delete</small>
</button>
</td>
</tr>
))
: files.map((file, index) => (
<tr key={file._id}>
<td>
<small>{file.title}</small>
</td>
<td>
<small>
{editable ? (
<input
type='text'
name='kind'
value={kind.str}
row={index}
onChange={(e) => onChange(e)}
/>
) : (
file.kind
)}
</small>
</td>
<td>
<small>{file.size}</small>
</td>
<td>
<small>{file.strength}</small>
</td>
<td>
<small>{file.combinations}</small>
</td>
<td>
<small>{file.favors}</small>
</td>
<td>
<small
className={file.availability ? 'alert-success' : 'alert-danger'}
>
{file.availability ? 'in stock' : 'out of stock'}
</small>
</td>
<td>
<small>{file.isCarousel ? 'carousel' : 'not carousel'}</small>
</td>
<td>
<small>{file.owner}</small>
</td>
<td>
<button
className='btn btn-dark'
onClick={(e) => onSubmit(e, file._id)}
>
<small>{editable ? 'save' : 'edit'}</small>
</button>
</td>
<td>
<button className='btn btn-danger'>
<small>delete</small>
</button>
</td>
</tr>
));
return <tbody>{list}</tbody>;
};