繁体   English   中英

为什么我的 React 组件渲染两次?

[英]Why is my React component rendering twice?

我正在使用React-hooksContext API进行 state 管理。 consumer组件呈现时,我已经控制台注销了context state。 console中,我注意到context中的 state 被注销了两次。 这是为什么?

我正在使用fetch API从我的本地node服务器中提取数据。 因此,在console的前几行中,我得到了context的初始 state,例如undefinednull - 然后就在它下面,我得到了从服务器中提取的更新后的 state。

我创建了一个“主” context来共享我将在整个应用程序中使用的functions fetch function 正在使用async/await


import React, { Component, createContext } from 'react';

export const MasterContext = createContext();

export class MasterProvider extends Component {
    state = {
        fetchData: async (url) => {
            let res = await fetch(url);
            let data = await res.json();
            return data;
        }
    }

    render() { 

        return ( 
            <MasterContext.Provider value={{...this.state}}>
                {this.props.children}
            </MasterContext.Provider>
         );
    }
}



我有两个组件,使用两个不同的contexts - 一个context非常简单


state = {
        title: '',
        body: '',
        cta: {
            text: ''
        },
        img: '',
        setHeaderPromo: (res) => {
            this.setState({
                title: res[0].title,
                body: res[0].body,
                cta: {...this.state.cta, ...res[0].cta},
                img: res[0].img
            });
        }
    }

为这个组件拉入的数据只是一个带有单个object的简单array

这是consumer组件:


const HeaderPromo = () => {
    const {title, body, cta, img, setHeaderPromo} = useContext(HeaderContext);
    const {fetchData} = useContext(MasterContext);

    useEffect(() => {

        fetchData(`http://localhost:5000/api/header`)
            .then((res) => {
                setHeaderPromo(res);
        });
    }, [fetchData, setHeaderPromo]);

    console.log(title);

    return ( 
        <article className="cp-header__promo">
            <div className="cp-header__promo__image">
                <img src={img} alt="promo"/>
            </div>
            <div className="cp-header__promo__copy">
                <h3>{title}</h3>
                <p>{body}</p>
                <button>{cta.text}</button>
            </div>
        </article>
     );
}

所以,这在技术上是可行的。 但是,我注意到当我注销title变量时,它会得到 output 两次。 第一次是context的初始 state ,第二次是输出数据的内容。 为什么这样做?

我的问题在于我的第二个组件 - context state 只是一个空array ,在fetch请求后被填充。

第二种context

 state = {
        albums: null,
        setNewReleases: (res) => {
            this.setState({
                albums: res
            });

        }
    }

这是两个consumer组件:

const NewReleases = () => {

    const {albums, setNewReleases} = useContext(NewReleaseContext);
    const {fetchData} = useContext(MasterContext);

    useEffect(() => {

        fetchData(`http://localhost:5000/api/new-releases`)
            .then((res) => {
                setNewReleases(res);
            });

    }, [fetchData, setNewReleases]);

  console.log('from newRelease component', albums);

    return ( 
        <section className="cp-new-releases">
            <Row sectionHeading={`New Releases`} albums={albums}/>
        </section>
     );
}

export default NewReleases;

因此, albums变量再次被注销两次。 首先,最初的 state,然后数据被拉入,并再次记录。

现在,我将这个albums变量作为prop传递给<Row/>组件。

行组件:

const Row = ({sectionHeading, albums}) => {

    console.log('from row component', albums);

    return ( 
        <Fragment>
            <div className="cp-new-releases__row">
            {(sectionHeading) && (<h3 className="row-title">{sectionHeading}</h3>)}
                <div className="cp-new-releases__row__album-container">

                {albums.map((item, index) => (
                        <Album img={item.img} title={item.title} key={index}/>
                  ))}

                </div> 
            </div>
        </Fragment>
     );
}

export default Row;

现在albums变量是一个包含两个objectsarray 如果我尝试遍历array ,它会抛出错误Uncaught TypeError: Cannot read property 'map' of null

我可以解决这个问题的唯一方法是进行if检查以查看albums是否具有价值。


const Row = ({sectionHeading, albums}) => {

    console.log('from row component', albums);

    return ( 
        <Fragment>
            <div className="cp-new-releases__row">
            {(sectionHeading) && (<h3 className="row-title">{sectionHeading}</h3>)}
                <div className="cp-new-releases__row__album-container">
                  {(albums) ? (
                    albums.map((item, index) => (
                        <Album img={item.img} title={item.title} key={index}/>
                  ))
                  ) : (<p style={{color: 'red'}}>no album</p>)}
                </div> 
            </div>
        </Fragment>
     );
}

export default Row;

它试图遍历context的初始 state 。 有人可以解释发生了什么,以及如何改进我的代码吗?

我知道这是冗长的。 所以,如果你坚持到最后,你就是冠军。

谢谢

FetchData 是一个异步操作 - 所以你的问题是你在从服务器获取数据之前添加了 Row 组件。 暂缓渲染<Row>直到您收集了您的相册。

return ( 
        <section className="cp-new-releases">
          { albums && // you might also want to test length here 
                      // - unless you want to show a message in your 
                      // Row component, indicating that non were found.
            <Row sectionHeading={`New Releases`} albums={albums}/>
          }
        </section>
     );

现在你的 Row 元素不是在初始加载时创建的,而是等到你得到数据,然后添加它。 想像一下,调用 function,您不会不必要地调用它,并且只能使用有效数据。 最终,这就是您将组件添加到渲染 function 时所做的事情。 所以你必须添加条件逻辑,因为组件是有效的函数,应该根据你的特定用例的逻辑来调用。

暂无
暂无

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

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