首页 > 解决方案 > 为什么我的 React 组件渲染两次?

问题描述

我正在使用React-hooksContext API状态管理。当组件呈现时,我已经控制台注销了context状态。consumerconsole我注意到状态从context被注销两次。这是为什么?

我正在使用从本地服务器fetch API中提取数据。node因此,在前几行中,console我得到了contexteg的初始状态undefinednull-然后就在它之下,我得到了从服务器拉入数据的更新状态。

我创建了一个“大师”context来分享functions我将在整个应用程序中使用的。该fetch功能正在使用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
            });
        }
    }

为这个组件提取的数据只是一个简单arrayobject.

这是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变量时,它会输出两次。第一次是初始状态context,第二次是输出数据的内容。为什么这样做?

我的问题在于我的第二个组件 -context状态只是一个在请求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变量再次被注销两次。首先,初始状态,然后数据被拉入,并再次记录。

现在,我将此albums变量<Row/>作为prop.

行组件:

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变量是一个array包含两个objects。如果我尝试遍历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. 有人可以解释发生了什么,以及如何改进我的代码吗?

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

谢谢

标签: javascriptreactjsreact-hooksreact-context

解决方案


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


推荐阅读