【某厂实习】任务管理系统开发-react框架Cloudscape云景开发2.md
项目难点
- 理清思路比较麻烦
- 表格配置比较麻烦
- 在使用useLocation时出现问题
github地址
这个基本算是完了吧, 还差几个页面和逻辑, 接下来十天我估计不会碰这个项目了, 写毕设去了.
uselocation问题复现
// tableConfig.js
{
id: "id",
header: "id",
cell: e => {
console.log("Link state:", { event: e });
return(
<Link
to={{
pathname: `/kpi/event/${e.id}`,
state: { event: e }
}}
>
{e.id}
</Link>
)
},
width: 110,
minWidth: 110,
sortingField: "id",
}
// EventDetail.js
const location = useLocation();
console.log("EventDetail location:", location);
const eventData = location.state?.event;
if (!eventData) {
return <div>No data available</div>;
}
在这个地方, 我的event是可以正常打印的.
但是我跳转过后去接受就出现了错误. 我的location的state是null.
我正确跳转了 因为线面的No data avilable正确显示
<Router>
<div>
<Link to="/kpi">Go to KPI Table</Link>
<Routes>
<Route path="/kpi" element={<KPITableView />} />
<Route path="/kpi/event/:id" element={<EventDetail />} />
</Routes>
</div>
</Router>
跳转也没有问题
我之前使用了传送id 然后通过useParams接受, 成功了.
找到问题了
- 我是这样解决的: 使用
最小复现实例方法来追踪定位bug来源 - 我使用了codesandbox.io这个沙盒来实现(这个沙盒直接提供了CRA框架)
- 引入reactrouterV6
- 创建组件Homepage.js, TestPage.js, NavigationButton.js
内容如下:
//Homepage.js
import React from 'react';
import { Link } from 'react-router-dom';
function HomePage() {
return (
<div>
<h1>Home Page</h1>
<Link to="/test">Go to Test Page with Link</Link>
</div>
);
}
export default HomePage;
//TestPage.js
import React from 'react';
import { useLocation } from 'react-router-dom';
function TestPage() {
const location = useLocation();
return (
<div>
<h1>Test Page</h1>
<p>Received state value: {location.state ? location.state.value : "No value passed"}</p>
</div>
);
}
export default TestPage;
// NavigationButton.js
// import React from 'react';
// import { useNavigate } from 'react-router-dom';
// function NavigationButton() {
// const navigate = useNavigate();
// const goToTestPage = () => {
// navigate('/test', { state: { value: 'This is a test value from button' } });
// };
// return <button onClick={goToTestPage}>Go to Test Page with Navigate</button>;
// }
// export default NavigationButton;
// import React from 'react';
// import { Link } from 'react-router-dom';
// function NavigationButton() {
// return (
// <Link
// to={{
// pathname: "/test",
// state: { value: 'This is a test value from Link' }
// }}
// >
// Go to Test Page with Link
// </Link>
// );
// }
// export default NavigationButton;
import React from 'react';
import { Link } from 'react-router-dom';
function NavigationButton() {
return (
<Link
to="/test"
state={{ value: 'This is a test value from Link' }}
>
Go to Test Page with Link
</Link>
);
}
export default NavigationButton;
// App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import HomePage from './HomePage';
import TestPage from './TestPage';
import NavigationButton from './NavigationButton';
function App() {
return (
<Router>
<div>
<NavigationButton />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/test" element={<TestPage />} />
</Routes>
</div>
</Router>
);
}
export default App;
我来解释一下
- 在app.js中 点击NavigationButton来判断如何传递对象可以获取到值.
- 在 v6中 useNavigate必须放在BroserRouter的后代里 stackoverflow.com/questions/7… 这里有源码解释
- 我这里本身不知道, 我把他写在了子组件里, 也可以正常运行.
- 这里关注NavigationButton组件的内容
- 刚开始我先这样写
import React from 'react';
import { useNavigate } from 'react-router-dom';
function NavigationButton() {
const navigate = useNavigate();
const goToTestPage = () => {
navigate('/test', { state: { value: 'This is a test value from button' } });
};
return <button onClick={goToTestPage}>Go to Test Page with Navigate</button>;
}
可以传值
- 后面我改成这样
import React from 'react';
import { Link } from 'react-router-dom';
function NavigationButton() {
return (
<Link
to={{
pathname: "/test",
state: { value: 'This is a test value from Link' }
}}
>
Go to Test Page with Link
</Link>
);
}
无法传值
-
锁定原因, 寻找Link和navigate传值的不同.
-
但是结果是可以互相使用, 查看link文档. 写法变了~
-
v6的写法改成了 state变成Link的属性, 写法如下
import React from 'react';
import { Link } from 'react-router-dom';
function NavigationButton() {
return (
<Link
to="/test"
state={{ value: 'This is a test value from Link' }}
>
Go to Test Page with Link
</Link>
);
}
export default NavigationButton;
这里记录一下:
使用useNavigate的原因是 在点击事件之后, 还需要处理额外的逻辑. 如果只需要传值就使用Link就好
Navigate的作用类似, 但是他是用来在额外逻辑触发后, 触发导航的.
table修改选中信息
// 修改选中信息.
onSelectionChange={({detail}) =>
setSelectedItems(detail.selectedItems)
}
selectedItems={selectedItems}
onSelectionChange={({detail}) => setSelectedMembers(detail.selectedItems)}
selectedItems={selectedMembers}
高阶函数来优化代码
因为我需要给三个相同的table来生成函数, 我这里直接用高阶函数即可
function addNewProject() {
const newProject = { name: "New Project", alt: "Description for new project" };
setProjects(prevProjects => [...prevProjects, newProject]);
}
function editSelectedProject() {
// 这里只是一个示例,你可能需要提供一个编辑界面或模态窗口来获取新的数据。
const newData = { name: "Edited Project", alt: "Edited Description" };
const updatedProjects = projects.map(project =>
project === selectedProjects[0] ? newData : project
);
setProjects(updatedProjects);
}
function deleteSelectedProject() {
const updatedProjects = projects.filter(project => project !== selectedProjects[0]);
setProjects(updatedProjects);
setSelectedProjects([]); // 重置选中的项目
}
//这里有个问题, 不能匹配对象, 要使用id匹配
所以这样的代码会好一些
function createTableHandlers(data, setData, selected, setSelected) {
return {
addNewItem: (newItem) => {
setData(prevData => [...prevData, newItem]);
},
editSelectedItem: (editedItem) => {
if (selected.length > 0) {
setData(prevData => prevData.map(item => item.id === selected[0].id ? editedItem : item));
}
},
deleteSelectedItem: () => {
if (selected.length > 0) {
setData(prevData => prevData.filter(item => item.id !== selected[0].id));
setSelected([]);
}
}
}
}
然后创建
//创建三个表格的handler
const projectsHandlers = createTableHandlers(projects, setProjects, selectedProjects, setSelectedProjects);
const stepsHandlers = createTableHandlers(steps, setSteps, selectedSteps, setSelectedSteps);
const membersHandlers = createTableHandlers(members, setMembers, selectedMembers, setSelectedMembers);
button绑定
<SpaceBetween direction="horizontal" size="xs">
<Button onClick={() => projectsHandlers.addNewItem({
id: uuidv4(),
name: "New Project",
alt: "Description for new project"
})}>Create Project</Button>
<Button onClick={() => projectsHandlers.editSelectedItem({
id: uuidv4(),
name: "Edited Project",
alt: "Edited Description"
})} disabled={selectedProjects.length === 0}>Edit</Button>
<Button onClick={projectsHandlers.deleteSelectedItem}
disabled={selectedProjects.length === 0}>Delete</Button>
</SpaceBetween>
还有一个最容易bug的点.
trackby是判断根据哪一个唯一值进行比较的, 这里要改成id
ok实现了.
bug和优化
表格重新渲染会导致表格宽度变化
- 关于列宽度的问题:
- 如果你想要使用固定列宽度,确保
resizableColumns属性设置为true。当启用此属性时,表格使用table-layout: fixed,列宽度将匹配由width和minWidth定义的值。 - 如果
resizableColumns设置为true,且列的width值未定义,则它们基于首次渲染的表格内容进行计算。随后的渲染(例如,异步加载)不会影响列宽度。因此,当使用可调整大小的列时,建议为所有列定义有意义的默认宽度,并在用户手动调整列大小时使用onColumnWidthsChange事件更新它们。 - 如果
resizableColumns未激活(默认设置),表格使用table-layout: auto。在这种情况下,呈现的列宽度取决于显示的内容,并且可能不匹配width和maxWidth的值。当显示的内容发生变化时,列宽度会自动更新。
- 如果你想要使用固定列宽度,确保
- 关于重新渲染和性能的问题:
- 使用
trackBy属性为表格的每一行指定一个唯一键。这对于性能优化很重要,因为React使用这些键来确定哪些行需要重新渲染。 - 如果可能的话,考虑使用客户端操作(如在客户端进行过滤、分页和排序)或服务器端操作,具体取决于要显示的数据量和需求。
- 使用
- 关于状态管理:
- 表格组件的选择和排序状态是受控的。你需要显式设置属性和相应的事件监听器。
- 使用
selectedItems属性和onSelectionChange事件监听器来管理选择状态。 - 使用
sortingColumn和sortingDescending属性以及onSortingChange事件监听器来管理排序状态。
- 关于编辑:
- 当用户提交内联编辑时,可以使用
submitEdit函数。如果返回一个promise,将在提交请求进行时保持加载状态。
- 当用户提交内联编辑时,可以使用
- 关于容器宽度和无限更新循环的问题:
- 表格组件会有条件地测量其宽度以应用样式。如果父容器是灵活的,这可能会导致一个无限的更新循环,并在用户界面中明显地产生组件闪烁。建议在一个容器中渲染表格,其宽度不由其内容决定。
因此在表格列配置加上宽度,然后再table里写上resizableColumns 即可
...props
resizableColumns={true}
{
id: "description",
header: "Description",
cell: item => item.alt,
sortingField: "alt",
width: 170,
minWidth: 165,
}
点击按钮不好点, 如何点击一行进行选择当前行
onRowClick={({detail}) => detail && detail.item && setSelectedProjects([detail.item])}