【问题标题】:TypeScript Error in React component on useState declaration and passing props to child component在使用状态声明和将道具传递给子组件时,React 组件中的 TypeScript 错误
【发布时间】:2022-01-24 15:33:22
【问题描述】:

我开始将 TypeScript 与 React 一起使用,目前正在构建一个示例应用程序进行练习,只是从公共 API 中获取数据并与 Material UI 一起实现 CRUD,但我在两个组件中遇到了错误,因此非常感谢您的帮助。

第一个错误是通过 props 将函数传递给子组件,第二个错误是使用我为我的数据类型创建的自定义接口声明 useState。

第一个组件是 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>
  );
};

还有第二个,子 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>
  );
};

您可能需要 IPost 接口,所以它是:

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

最后是 IEditPost 界面:

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

提前谢谢大家!

【问题讨论】:

  • 你能提供IEditPostDialog吗?第一个错误基本上是您使用空对象初始化IPost 类型的状态。但是界面希望您的所有属性都存在。您可以通过在属性名称后附加 ? 来省略它。
  • 对不起,完全忘记了第二个接口。我编辑了我的问题,你现在可以看到了。

标签: javascript reactjs typescript


【解决方案1】:

第二个错误基本上是你用一个空对象初始化了IPost类型的状态。但是界面希望您的所有属性都存在。您可以通过在属性名称后附加 ? 来省略它。

你可以简化你的子组件来解决这个问题:

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

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

如果您遇到其他问题: 首先,您不需要明确指定null

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

我们需要查看接口IEditPostDialog,看看将值selectedPost 传递给底层组件有什么问题。

【讨论】:

  • 对不起,完全忘记了第二个接口。我编辑了我的问题,你现在可以看到了。
  • 谢谢,在这种情况下,只需将&lt;IPost | null&gt; 更改为&lt;IPost&gt; 或更改接口IEditPostDialog 中的预期类型。那是因为&lt;IPost | null&gt; !== &lt;IPost&gt;
  • 这是我的第一个想法,但是当我尝试这个时,linter 在那个 null 处抛出一个错误,说“null”类型的参数不能分配给 IPost 类型的参数
  • 这就是为什么我说“或更改界面中的预期类型”。您需要具有相同的类型签名。
猜你喜欢
  • 2020-10-08
  • 2017-04-30
  • 2017-01-31
  • 2023-02-01
  • 1970-01-01
  • 2021-04-05
  • 2019-01-20
  • 2022-01-18
  • 1970-01-01
相关资源
最近更新 更多