简体   繁体   English

以正确的顺序放置反应钩子

[英]Placing react hooks in proper order

I'm working with a component that displays a list of blogs.我正在使用一个显示博客列表的组件。 Then for each blog, I map over each category in an array and display that.然后对于每个博客,我 map 在数组中的每个类别上显示并显示。 The code so far is:到目前为止的代码是:

import React, { useState, useEffect } from "react";
import { connect } from "frontity";
import { GridWrap, GridRow, GridColumn } from "emotion-flex-grid";
import Link from "@frontity/components/link";
import CardContainer from "./index.style";

const Card = ({ state, post, libraries, categoryName }) => {
  console.log("post", post);

  const [categoryNameFromId, setCategoryNameFromId] = useState();
  useEffect(() => {
    fetch(`${state.source.api}wp/v2/categories/${cat}`)
      .then((response) => response.json())
      .then((data) => {
        setCategoryNameFromId(data);
      });
  }, []);

  return (
    <GridColumn width={[12, 12, 6, 4, 4]} p={["none", "none", "s", "m"]} py={["s", "s", "s", "m"]} style={{display: `flex`, alignSelf: `stretch`}}>
      <CardContainer>
        <div className="info">
          {post.acf.featured_media ? <img src={post.acf.featured_media.url} /> : <img src="https://images.unsplash.com/photo-1510337550647-e84f83e341ca?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=3289&q=80" />}
          <Link link={`blog/${post.slug}`} className="title">
            {post.title.rendered}
          </Link>
          <GridRow className="date-time">
            <p className="date">{post.formatted_date}</p>
            <p className="reading-time">reading time</p>
          </GridRow>
          <div className="excerpt"><libraries.html2react.Component html={post.excerpt.rendered} /></div>
        </div>

        <div className="share">
          <GridRow justify="between">
            <p>
            {post.categories.map((cat) => {
              return (
                <>
                  <span>{categoryNameFromId && categoryNameFromId.name}, </span>
                </>
              )
            })}
            </p>
            <div className="social-icons">
              <i className="icon kap-facebook" />
              <i className="icon kap-twitter" />
              <i className="icon kap-linkedin" />
              <i className="icon kap-mail" />
            </div>
          </GridRow>
        </div>
      </CardContainer>
    </GridColumn>
  )
}

export default connect(Card);

The issue I am having though, is that I get an error in the console saying that "Uncaught ReferenceError: cat is not defined".我遇到的问题是,我在控制台中收到一个错误,提示“未捕获的 ReferenceError:cat 未定义”。 So I tried moving the hook to a different spot, like this:所以我试着把钩子移到另一个地方,像这样:

import React, { useState, useEffect } from "react";
import { connect } from "frontity";
import { GridWrap, GridRow, GridColumn } from "emotion-flex-grid";
import Link from "@frontity/components/link";
import CardContainer from "./index.style";

const Card = ({ state, post, libraries, categoryName }) => {
  console.log("post", post);

  return (
    <GridColumn width={[12, 12, 6, 4, 4]} p={["none", "none", "s", "m"]} py={["s", "s", "s", "m"]} style={{display: `flex`, alignSelf: `stretch`}}>
      <CardContainer>
        <div className="info">
          {post.acf.featured_media ? <img src={post.acf.featured_media.url} /> : <img src="https://images.unsplash.com/photo-1510337550647-e84f83e341ca?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=3289&q=80" />}
          <Link link={`blog/${post.slug}`} className="title">
            {post.title.rendered}
          </Link>
          <GridRow className="date-time">
            <p className="date">{post.formatted_date}</p>
            <p className="reading-time">reading time</p>
          </GridRow>
          <div className="excerpt"><libraries.html2react.Component html={post.excerpt.rendered} /></div>
        </div>

        <div className="share">
          <GridRow justify="between">
            <p>
            {post.categories.map((cat) => {

                const [categoryNameFromId, setCategoryNameFromId] = useState();
                useEffect(() => {
                  fetch(`${state.source.api}wp/v2/categories/${cat}`)
                    .then((response) => response.json())
                    .then((data) => {
                      setCategoryNameFromId(data);
                    });
                }, []);

              return (
                <>
                  <span>{categoryNameFromId && categoryNameFromId.name}, </span>
                </>
              )
            })}
            </p>
            <div className="social-icons">
              <i className="icon kap-facebook" />
              <i className="icon kap-twitter" />
              <i className="icon kap-linkedin" />
              <i className="icon kap-mail" />
            </div>
          </GridRow>
        </div>
      </CardContainer>
    </GridColumn>
  )
}

export default connect(Card);

And the error did go away.并且错误确实 go 消失了。 But I am pretty sure this is not the correct way to do this.但我很确定这不是正确的方法。 Isn't there a way to have the hook be at the top of the component?有没有办法让钩子位于组件的顶部? Any input would be great!任何输入都会很棒!

The calls to API are used to be in the useEffect when the component has been mounted, in this case you can move the useEffect outside of your JSX and call from there the API.当组件已安装时,对 API 的调用用于在useEffect中,在这种情况下,您可以将useEffect移到 JSX 之外并从那里调用 API。

     useEffect(() => {
         const promises = post.categories.map(cat => {
               fetch(`${state.source.api}wp/v2/categories/${cat}`)
                .then((response) => response.json());
         });

         Promise.all(promises).then(data => data.map(item => setCategoryNameFromId(item);))
              
     }, []);

You need to store the result of the all api calls.您需要存储所有 api 调用的结果。 For every post.categories item fetch the data and save it, once you have done, update the categories state:对于每个 post.categories 项获取数据并保存,一旦完成,更新类别 state:

  const [categories, setCategories] = useState([]);
  useEffect(() => {
    const fetchData = async (cat) => {
       const categoryIdData = await fetch(`${state.source.api}wp/v2/categories/${cat}`).then(res => res.json())
       return categoryIdData;
    }
    
   const updateData = async () => {
      let categories = [];
      post.categories.map(async (cat) => {
         const catData = await fetchData(cat);
         categories.push(catData);
      });
      setCategories(categories); 
   }

   updateData();
  }, []);

then in the template:然后在模板中:

 <p>
   {categories.map((cat) => (<>
        <span>{cat.name}, </span>
       </>)
   }
 </p>

Of course, you can simplify this using Promise.all :当然,您可以使用Promise.all来简化它:

    const [categories, setCategories] = useState([]);
    useEffect(() => {
       const updateData = async () => {
       const categoriesFetch = post.categories.map(cat => fetch(`${state.source.api}wp/v2/categories/${cat}`).then(res => res.json()));
       const categories = await Promise.all(categoriesFetch);
       setCategories(categories); 
    }
    
    updateData();
   }, []);

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

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