代码之家  ›  专栏  ›  技术社区  ›  Mel

如何使用带有firestore数据的react钩子从索引链接到show视图

  •  0
  • Mel  · 技术社区  · 4 年前

    我试图找出如何定义一个指向引用的链接,该链接可以使用firebase文档id链接到该文档的显示视图。我可以呈现索引。我找不到定义文档链接的方法。

    我已经跟踪了 this tutorial -这是很好的得到除显示视图以外的CRUD步骤。我能找到其他的 tutorials 用类组件来实现这一点,我用钩子所能找到的最接近的就是这个不完整 project repo .

    我想尝试在索引中添加一个链接,以便在新视图中显示文档。

    我有一个索引:

      const useBlogs = () => {
    const [blogs, setBlogs] = useState([]); //useState() hook, sets initial state to an empty array
      useEffect(() => {
        const unsubscribe = Firebase
          .firestore //access firestore
          .collection("blog") //access "blogs" collection
          .where("status", "==", true)
          .orderBy("createdAt")
          .get()
          .then(function(querySnapshot) {
        //   .onSnapshot(snapshot => {
            //You can "listen" to a document with the onSnapshot() method.
            const listBlogs =  querySnapshot.docs.map(doc => ({
              //map each document into snapshot
              id: doc.id, //id and data pushed into blogs array
              ...doc.data() //spread operator merges data to id.
            }));
            setBlogs(listBlogs); //blogs is equal to listBlogs
          });
        return 
        // () => unsubscribe();
      }, []);
      return blogs;
    };
    
    const BlogList = ({ editBlog }) => {
      const listBlog = useBlogs();
      return (
        <div>
          {listBlog.map(blog => (
            <Card key={blog.id} hoverable={true} style={{marginTop: "20px", marginBottom: "20px"}}>
    
                <Title level={4} >{blog.title} </Title>
                <Tag color="geekblue" style={{ float: "right"}}>{blog.category} </Tag>
                <Paragraph><Text>{blog.caption}
    
                </Text></Paragraph> 
                <Link to={`/readblog/${blog.id}`}>Read</Link>
                <Link to={`/blog/${blog.id}`}>Read</Link>
            </Card>       
          ))}
          </div>
      );
    };
    export default BlogList;
    

    然后我定义了一条路线:

    export const BLOGINDEX = '/blog';
    export const BLOGPOST = '/blog/:id';
    export const NEWBLOG = '/newblog';
    export const EDITBLOG = '/editblog';
    export const VIEWBLOG = '/viewblog';
    export const READBLOG = '/readblog/:id';    
    

    我找不到用钩子做这个的教程。有人知道如何从索引链接到我可以在不同页面中显示的文档吗?

    我找到这个了 code sandbox . 它看起来像是在updateCustomer页面中呈现一个干净的页面,并使用索引中的数据来完成这项工作—但是对于我来说,如果不解释正在发生的事情(特别是updateCustomer文件定义了一个setCustomer变量,引用useForm-但是useForm中没有这个定义。该变量用于文件中试图标识数据的关键部分,因此我无法模拟这些步骤。

    下一次尝试

    我找到这个了 blog post 这就为相关文档的定位提出了一些建议。

    我实现了这些更改,虽然我可以打印正确的文档.id在“已读”页上,我找不到访问文档属性的方法(例如:博客.title).

    import React, { useHook } from 'react';
    import {
        useParams
      } from 'react-router-dom';
    
    import Firebase from "../../../firebase";
    import BlogList from './View';
    
    
    function ReadBlogPost() {
        let { slug } = useParams()
            // ...
    
    
        return (
            <div>{slug} 
            </div>
        )
    };
    
    export default ReadBlogPost;
    

    下一次尝试:

    我试着用鼻涕虫文件id要获取post文档,请执行以下操作:

    import React, { useHook, useEffect } from 'react';
    import {
        useParams
      } from 'react-router-dom';
    
    
    
    import Firebase from "../../../firebase";
    import BlogList from './View';
    
    
    function ReadBlogPost() {
        let { slug } = useParams()
            // ...
        useEffect(() => {
            const blog = 
        Firebase.firestore.collection("blog").doc(slug);
    
                blog.get().then(function(doc) {
                    if (doc.exists) {
                        console.log("Document data:", doc.data());
    doc.data();
                    } else {
                        // doc.data() will be undefined in this case
                        console.log("No such document!");
                    }
                }).catch(function(error) {
                    console.log("Error getting document:", error);
                });
            });
    
    
            return (
                <div>{blog.title} 
                </div>
            )
        };
    
        export default ReadBlogPost;
    

    它返回一个错误,说博客没有定义。我也想回去{文件标题}但我也犯了同样的错误。我可以在控制台上看到所有的数据。

    我真的无法理解编写文档的意义——我无法找到破译指令的起点,所以我学到的大多数东西都是经过反复尝试的,但我已经找不到地方去寻找尝试新东西的灵感。

    下一次尝试

    我下一步要做的就是 tutorial .

    function ReadBlogPost(blog) {
        let { slug } = useParams()
            // ...
        useEffect(() => {
            const blog = 
    Firebase.firestore.collection("blog").doc(slug);
    
            blog.get().then(function(doc) {
                if (doc.exists) {
                    doc.data()
                    console.log("Document data:", doc.data());
                } else {
                    // doc.data() will be undefined in this case
                    console.log("No such document!");
                }
            }).catch(function(error) {
                console.log("Error getting document:", error);
            });
    
        },
        [blog]
    );
    
    
        return (
            <div><Title level={4} > {blog.title}
    
            </Title>
    
            <p>{console.log(blog)}</p>
    
    
            </div>
        )
    };
    
    export default ReadBlogPost;
    

    当我尝试这个的时候,唯一奇怪的是控制台.loguseffect方法内部精确地给出了所有数据,但是当我在return方法内部记录它时,我得到了一堆乱七八糟的数据(如下图所示)。 enter image description here

    下一次尝试

    我找到这个了 tutorial ,它使用实时数据库而不是firestore,但我试图复制逻辑。

    import React, { useHook, useEffect, useState } from 'react';
    import {
        useParams
      } from 'react-router-dom';
    
    import Firebase from "../../../firebase";
    import BlogList from './View';
    import { Card, Divider, Form, Icon, Input, Switch, Layout, Tabs, Typography, Tag, Button } from 'antd';
    const { Paragraph, Text, Title } = Typography;
    
    
     const ReadBlogPost = () => {
        const [loading, setLoading] = useState(true);
        const [currentPost, setCurrentPost] = useState();
    
    
    
        let { slug } = useParams()
    
        if (loading && !currentPost) {
            Firebase
              .firestore
              .collection("blog")
              .doc(slug)
              .get()
              .then(function(doc) {
                if (doc.exists) {
                    setCurrentPost(...doc.data());
                    console.log("Document data:", doc.data());
                }
              }),  
    
              setLoading(false)
              }
    
              if (loading) {
                return <h1>Loading...</h1>;
              }
    
    
    
    
    
        return (
            <div><Title level={4} > 
            {currentPost.caption}
            {console.log({currentPost})}
    
            </Title>
    
    
    
    
            </div>
        )
    
    };
    export default ReadBlogPost;
    

    也许这篇博文已经过时了,也许是因为我在.jsx中使用了.js,我认为这意味着我不能使用 if statements ,但我也不能让它工作。错误显示:

    第21:9行:应为赋值或函数调用,而应为saw 表达式没有未使用的表达式

    它指向从Firebase开始的那条线。

    我去掉了所有的加载位,试图使数据呈现。现在可以消除上面的错误信息。但是,我仍然无法返回currentPost的值。

    在return语句中,我无法输出{当前职务}-当我试图输出{currentPost}时,错误消息显示:

    错误:对象作为反应子对象无效(找到:具有键的对象 {标题,类别,创建日期,文章,状态,标题})。如果你想的话 呈现子项集合,请改用数组。

    这没道理!我很想知道为什么我可以在return语句之前记录这些值,而在return语句内部,我可以将它们记录在对象上,但是我找不到如何将它们记录为属性。

    0 回复  |  直到 4 年前
        1
  •  0
  •   Giorgio Zanni    4 年前

    首先:你的 useBlog() 返回预期数据的钩子?如果是的话,你只需要定义 <Link/> 部件正确。

    <Link 
      // This will look like /readblog/3. Curly braces mean
      // that this prop contains javascript that needs to be
      // evaluated, thus allowing you to create dynamic urls.
      to={`/readblog/${blog.id}`}
      // Make sure to open in a new window
      target="_blank"
    >
      Read
    </Link>
    

    编辑:如果要将数据传递给新组件,则需要设置一个存储,以避免两次获取同一资源(一次是在装载列表时,另一次是在装载BlogPost本身时)

    // Define a context
    const BlogListContext = React.createContext()
    
    // In a top level component (eg. App.js) define a provider
    const App = () => {
      const [blogList, setBlogList] = useState([])
    
      return (
        <BlogListContext.Provider value={{blogList, setBlogList}}>
          <SomeOtherComponent/>
        </BlogListContext.Provider>
      )
    }
    
    // In your BlogList component
    const BlogList = ({ editBlog }) => {
      const { setBlogList } = useContext(BlogListContext)
      const listBlog = useBlogs()
    
      // Update the blog list from the context each time the
      // listBlog changes
      useEffect(() => {
        setBlogList(listBlog)
      }, [listBlog])
      return (
        // your components and links here
      )
    }
    
    // In your ReadBlog component
    const ReadBlogComponent = ({ match }) => {
      const { blogList } = useContext(BlogListContext)
      // Find the blog by the id from params.
      const blog = blogList.find(blog => blog.id === match.params.id) || {}
    
      return (
        // Your JSX
      )
    }
    

    还有其他传递数据的选项:

    1. 通过url参数(不推荐)。
    2. 只需传递ID并让组件在挂载时获取自己的数据。
        2
  •  0
  •   Mel    4 年前

    我找到了一个对时间戳以外的每个属性都有效的答案。

    const [currentPost, setCurrentPost] = useState([]);
    

    useState()初始化状态中有一个空数组。

    关于时间戳-我用firestore时间戳经历了这么多次-最近 here . 2019年12月奏效的解决方案不再奏效。又开始扯我的头发了。。。