将异步获取的数据传递给子道具

Passing asynchronously acquired data to child props

本文关键字:异步 获取 数据      更新时间:2023-09-26

我正在制作一个应用程序,该应用程序从远程源获取一系列新闻项目,并将其显示在页面上。

我有端点,可以使用$.getJSON()进行成功的调用,控制台日志证明了这一点。我将此调用放入父组件,因为子组件将需要使用数据。

然而,当我将这些数据传递给子组件时,控制台错误出现:

Uncaught TypeError: Cannot read property 'headline' of undefined

这是因为React甚至在数据传递到组件之前就试图渲染它。这让我认为我应该首先在componentDidMount之外的其他地方调用我的ajax。

为了解决这个问题,我在子组件上设置了一个方法,如果道具存在,该方法会返回标题:

getHeadline: function () {
    if(this.props.newsItems){
        return this.props.newsItems.headline
    } else {
        return null
    }
},

这感觉有点令人讨厌。有更好的方法吗,或者我的代码中遗漏了什么?

var BigStory = React.createClass({
    getHeadline: function () {
        if(this.props.newsItems){
            return this.props.newsItems.headline
        } else {
            return null
        }
    },
    render: function () {
        console.log('props:', this.props);
        console.log('newsItems:', this.props.newsItems);
        return (
            <div className="big-story col-xs-12">
                <div className="col-sm-5">
                    <h1>{this.getHeadline()}</h1>
                    <p>Placeholder text here for now.</p>
                    <p>time | link</p>
                </div>
                <div className="col-sm-7">
                    <img src="http://placehold.it/320x220" alt=""/>
                </div>
            </div>
        );
    }
});
var Main = React.createClass({
    getInitialState: function () {
        return {
            newsItems: []
        }
    },
    componentDidMount: function () {
        this.getNewsItems();
    },
    getNewsItems: function () {
        $.getJSON('http://www.freecodecamp.com/news/hot', (data) => {
            console.log('data sample:', data[0]);
            this.setState({newsItems: data})
        })
    },
    render: function () {
        return (
            <div className="container">
                <div className="main-content col-sm-12">
                    <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                        <BigStory newsItems={this.state.newsItems[0]}/>
                    </div>
                </div>
            </div>
        );
    }
});

我建议让父级决定当它处于"加载"状态时该做什么,并将BigStory作为一个"哑"组件,它总是呈现相同的内容,假设它总是收到一个有效的newsItem

在这个例子中,我展示了一个<LoadingComponent />,但它可以是你需要的任何东西。概念是BigStory不必担心"接收无效数据"的边缘情况。

var Main = React.createClass({
  // ...
  render() {
    const {newsItems} = this.state;
    // You could do this, pass down `loading` explicitly, or maintain in state
    const loading = newsItems.length === 0;
    return (
      <div className="container">
          <div className="main-content col-sm-12">
              <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                  {loading 
                    ? <LoadingComponent />
                    : <BigStory newsItem={newsItems[0]} />  
                  }
              </div>
          </div>
      </div>
    );
  }
});
function BigStory(props) {
  // Render as usual. This will only be used/rendered w/ a valid
  return (
    <div className="big-story col-xs-12">
      <h1>{props.headline}</h1>
      {/* ... */}
    </div>
  )
}

另一种解决方案(尽管我推荐一种更像上面的方法)是始终以相同的方式使用BigStory组件,但在没有加载故事时为其提供一个"占位符故事"。

const placeholderNewsItem = {
  headline: 'Loading...',
  /* ... */
};
var Main = React.createClass({
  // ...
  render() {
    const {newsItems} = this.state;
    // Conditionally pass BigStory a "placeholder" news item (i.e. with headline = 'Loading...')
    const newsItem = newsItems.length === 0
      ? placeholderNewsItem
      : newsItems[0];
    return (
      <div className="container">
          <div className="main-content col-sm-12">
              <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                  <BigStory newsItem={newsItem} />
              </div>
          </div>
      </div>
    );
  }
});

这是一个非常常见的场景。约翰·卡彭特的方法会奏效。我经常使用的另一种方法是使用微调器图像创建某种类型的Loading组件(或者其他任何可以用来表示数据正在传输的方法)。然后,当客户端等待数据到达时,您可以呈现Loading组件。例如:

render: function () {
    if (this.state.newsItems.length === 0) return <Loading />;
    return (
        <div className="container">
            <div className="main-content col-sm-12">
                <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                    <BigStory newsItems={this.state.newsItems[0]}/>
                </div>
            </div>
        </div>
    );
}

一种方法是将this.props.newsItems.headline设置为"loading…",以向用户指示数据即将到来。