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

时间:2020-05-21 08:39:01

标签: javascript reactjs react-hooks react-context

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

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

我创建了一个“母版” context,以共享我将在整个应用程序中使用的functionsfetch函数正在使用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
            });
        }
    }

为此组件提取的数据只是一个简单的array和一个object

这是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的{​​{1}}。如果我尝试遍历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; 的初始状态。有人可以解释发生了什么事,以及如何改善我的代码吗?

我知道这很困难。因此,如果您最终做到这一点,那您将是一个冠军。

谢谢

1 个答案:

答案 0 :(得分:2)

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元素不是在初始加载时创建的,而是要等到获得数据后再添加它。可以将其想像成是调用一个函数,而不必仅使用有效数据调用它。最终,这就是将组件添加到渲染函数时要执行的操作。因此,您必须添加条件逻辑,因为组件是有效的功能,应根据您特定用例的逻辑进行调用。