【发布时间】:2020-10-18 18:53:59
【问题描述】:
我正在尝试在 React 和 material-ui 中构建一个主/详细类型用例的原型,使用一个简单的对象列表,以及一个用于编辑和保存列表中项目的对话框。我正在更新访问,用户对象的子对象,但我不知道如何 1. 将修改后的访问项与父用户对象集成回 2. 我将在哪里执行此操作以及我将在哪里创建 api调用以使用更新的访问对象保存用户。
我的测试 api 看起来像:
- GET /users/ => 将用户对象返回为:{id, name, visit:[{id,visited}]}
- PUT /users/ 相同的对象
我正在使用 3 个主要组件:
- UserVisitList.tsx(将用户对象的 api 与“访问”对象数组匹配)
- 带有编辑图标按钮的单个“访问”项目 (UserVisit.tsx)
- 一个编辑对话框,用于更改“访问”中的一个状态 (EditVisitDialog.tsx)。
- useEffect 中的 GetUser 函数只是一个文件,它返回一个带有访问量的用户对象。
UserVisitList.tsx(这个放在 App.tsx 中)
export const UserVisitList: React.FC = () => {
const [user, setUser] = useState<User>({});
const [open, setOpen] = useState<boolean>(false);
const [visit, setVisit] = useState<Visit>({});
const onOkClick = (visit: Visit) => {
// set the active visit?
setVisit(visit);
setOpen(true);
};
const onDialogCloseClick = () => {
setOpen(false);
};
const onChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
// update the visited prop of active visit
setVisit({ ...visit, visited: e.currentTarget.value });
};
useEffect(() => {
setUser(GetData("Jane"));
}, []);
return (
<div>
<Typography variant="h4">Visits for {user.name}</Typography>
<List>
<>
<Grid key={user.id} container>
<Grid item xs={12}>
{user.visits?.map(function (visit: Visit, i) {
return (
<UserVisit
key={visit.id}
visit={visit}
onEditClick={() => onOkClick(visit)}
/>
);
})}
</Grid>
</Grid>
{open && (
<EditVisitDialog
visit={visit}
name={user.name}
onChangeHandler={onChangeHandler}
onCloseHandler={onDialogCloseClick}
></EditVisitDialog>
)}
</>
</List>
</div>
);
};
export default UserVisitList;
UserVisit.tsx:
interface IVisitProp {
visit: Visit;
onEditClick: () => void;
}
export default function UserVisit({ visit, onEditClick }: IVisitProp) {
return (
<ListItem>
<ListItemText primary={visit.visited} />
<ListItemIcon>
<IconButton onClick={onEditClick} edge="start">
<EditIcon />
</IconButton>
</ListItemIcon>
<ListItemSecondaryAction>
<IconButton onClick={() => console.log("del not implemented")}>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
);
}
EditVisitDialog.tsx:
interface IProps {
visit: Visit;
name?: string;
onChangeHandler: (e: React.ChangeEvent<HTMLInputElement>) => void;
onCloseHandler: () => void;
}
export default function EditVisitDialog({
visit,
name,
onChangeHandler,
onCloseHandler
}: IProps) {
return (
<div>
<Dialog open={true}>
<DialogTitle style={{ textAlign: "center" }}>
{" "}
Edit Visit for {name}
</DialogTitle>
<DialogContent>
<div>
<label>Visited </label>
<input
type="text"
onChange={onChangeHandler}
value={visit.visited}
/>
</div>
</DialogContent>
<DialogActions>
<Grid container justify="center">
<Button onClick={onCloseHandler} variant="outlined">
Close
</Button>
<Divider />
<Button onClick={onCloseHandler} variant="outlined">
Save
</Button>
</Grid>
</DialogActions>
</Dialog>
</div>
);
}
我感觉我做错了,就像我处理状态的方式一样。我把代码放在一个半工作的沙箱里:https://codesandbox.io/s/master-detail-with-dialog-wnlsz。我无法将更新后的访问对象返回给用户。而且我还想知道一旦我将访问连接回用户,我会在哪里调用以将用户保存到 api。
我不应该这样做吗?如果有的话,有什么更直接的方式来处理这个简单的用例。就像我说的,我认为我做的不太对。在 React 中更新数据与我习惯的非常不同(例如通过回调获取 id、更新 dom、ajax 等)。
【问题讨论】:
-
您的整体设置看起来不错! onChange 处理程序应将值设置为本地状态,因为您不想使用键入的每个字符调用 API。您应该在 onDialogCloseClick 或 useEffect 挂钩中调用 API。无论哪种方式,您都想在调用之前检查数据是否实际发生了变化,以避免不必要的调用,因此您希望将获取的版本作为变量存储在某处。
-
@LindaPaiste 谢谢!我仍然不知道如何访问 User 对象。如何将其附加到用户对象,以便正确更新对用户对象的访问?因此,假设您将其中一次访问更新为“2019 年 11 月 10 日”。当他们点击完成时,我希望用户对象为:{ id: x, name: 'name',visited: [ {visited: "November 10th, 2019"} // changed visit ] }
标签: reactjs typescript master-detail use-state