简体   繁体   English

TypeScript React 组件在 useState 声明和将道具传递给子组件时出错

[英]TypeScript Error in React component on useState declaration and passing props to child component

I started using TypeScript with React and currently building a sample App for practice, just fetching data from public API and implementing CRUD alongside with Material UI but I'm facing errors in two components, so I would much appreciate your help.我开始将 TypeScript 与 React 一起使用,目前正在构建一个示例应用程序进行练习,只是从公共 API 获取数据并与 Material UI 一起实现 CRUD,但我在两个组件中遇到错误,因此非常感谢您的帮助。

The first error is on passing function via props to a child component, and the second one is on declaring useState using a custom Interface I created for my data type.第一个错误是通过 props 将 function 传递给子组件,第二个错误是使用我为我的数据类型创建的自定义接口声明 useState。

The first component is PostTable.tsx第一个组件是 PostTable.tsx

const PostsTable: React.FunctionComponent = () => {
  const [posts, setPosts] = useState<Array<IPost>>([]);
  const [selectedPost, setSelectedPost] = useState<IPost | null>(null);
  const [dialogOpen, setDialogOpen] = useState<boolean>(false);

  const fetchPosts = () => {
    PostService.getAllPosts()
      .then((response: any) => {
        setPosts(response.data);
      })
      .catch((e: Error) => {
        console.log(e);
      });
  };

  useEffect(() => {
    fetchPosts();
  }, [posts]);

  const editPost = (post: IPost) => (event: any) => {
    setSelectedPost(post);
    setDialogOpen(true);
  };

  const handleClose = () => {
    setDialogOpen(false);
  };

  const deletePost =
    (id: number): any =>
    (event: Event) => {
      event.stopPropagation();
      PostService.deletePost(id)
        .then((response: any) => {
          setPosts(response.data);
          console.log(posts);
        })
        .catch((e: Error) => {
          console.log(e);
        });
    };

  return (
    <Container fixed>
      {!posts || posts.length < 1 ? (
        <div style={{ display: 'flex', justifyContent: 'center' }}>
          <CircularProgress color="primary" size={100} />
        </div>
      ) : (
        <TableContainer component={Paper}>
          <Table sx={{ minWidth: 650 }} aria-label="simple table">
            <TableHead>
              <TableRow>
                <TableCell align="left">User Id</TableCell>
                <TableCell align="left">Post Id</TableCell>
                <TableCell align="left">Title</TableCell>
                <TableCell align="left">Actions</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {posts.map((post: IPost) => (
                <TableRow
                  key={post.id}
                  sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
                >
                  <TableCell>{post.userId}</TableCell>
                  <TableCell align="left">{post.id}</TableCell>
                  <TableCell align="left">{post.title}</TableCell>
                  <TableCell align="left">
                    <Tooltip title="Delete">
                      <IconButton onClick={editPost(post)}>
                        <EditIcon />
                      </IconButton>
                    </Tooltip>
                    <Tooltip title="Edit">
                      <IconButton onClick={deletePost(post.id)}>
                        <DeleteIcon />
                      </IconButton>
                    </Tooltip>
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>
      )}
      {
        <EditPostDialog
          open={dialogOpen}
          handleClose={handleClose}
          /**Error is on this line**/
          selectedPost={selectedPost}
        />
      }
    </Container>
  );
};

And also second, the child EditPostDialog.ts其次,子 EditPostDialog.ts

const EditPostDialog: React.FunctionComponent<IEditPostDialog> = (
  props: IEditPostDialog
) => {
  const { open, handleClose, selectedPost } = props;
  /**Error on this line**/
  const [post, setPost] = useState<IPost>({});

  useEffect(() => {
    const newPost = {
      id: selectedPost.id,
      userId: selectedPost.userId,
      title: selectedPost.title,
      body: selectedPost.body,
    };
    setPost(newPost);
  }, [selectedPost]);

  const handleChange = (event: any) => {
    setPost({ ...post, [event.target.name]: event.target.value });
  };

  const handleSubmit = () => {
    PostService.updatePost(post.id, post);
    handleClose();
  };

  return (
    <Dialog onClose={handleClose} open={open}>
      <DialogTitle id="simple-dialog-title">Post info</DialogTitle>
      <DialogContent classes={{ root: 'dialog-content' }}>
        <TextField
          id="id"
          label="Post Id"
          name="id"
          variant="outlined"
          value={post.id}
          onChange={handleChange}
        />
        <TextField
          id="userId"
          label="User Id"
          name="userId"
          variant="outlined"
          value={post.userId}
          onChange={handleChange}
        />
        <TextField
          id="title"
          label="Title"
          name="title"
          variant="outlined"
          value={post.title}
          onChange={handleChange}
        />
        <TextField
          id="body"
          label="Body"
          name="body"
          variant="outlined"
          value={post.body}
          onChange={handleChange}
        />
      </DialogContent>
      <DialogActions>
        <Button onClick={handleClose}>Close</Button>
        <Button onClick={handleSubmit}>Submit</Button>
      </DialogActions>
    </Dialog>
  );
};

You probably need IPost interface, so there it is:您可能需要 IPost 接口,所以它是:

export default interface IPost {
  userId: number;
  id: number;
  title: string;
  body: string;
}

And at the end IEditPost interface:最后是 IEditPost 界面:

export default interface IEditPostDialog {
  open: boolean;
  handleClose: () => void;
  selectedPost: IPost;
}

Thank you all in the advance!提前谢谢大家!

The second error is basically that you initialize the state of type IPost with an empty object.第二个错误基本上是您使用空的 object 初始化 IPost 类型的IPost But the interface expects all of your properties to be present.但是界面希望您的所有属性都存在。 You can omit that by appending a ?您可以通过附加? after the name of the property.在物业名称之后。

You can simplify your child component to solve this:您可以简化您的子组件来解决这个问题:

const [post, setPost] = useState<IPost>(selectedPost);

  useEffect(() => {
    setPost(selectedPost);
  }, [selectedPost]);

In case of your other problem: First of all, you do not need to specify null explicitly:如果您遇到其他问题:首先,您不需要明确指定null

// Your code: const [selectedPost, setSelectedPost] = useState<IPost | null>(null);
const [selectedPost, setSelectedPost] = useState<IPost>(null); // should work perfectly fine

We need to see the interface IEditPostDialog to see whats the problem with passing the value selectedPost to the unterlying component.我们需要查看接口IEditPostDialog以了解将值selectedPost传递给底层组件的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM