将AJAX结果作为道具传递给子组件

时间:2015-02-18 22:46:34

标签: javascript ajax asynchronous reactjs

我试图在React中创建一个博客。在我的主ReactBlog组件中,我正在对节点服务器进行AJAX调用以返回一组帖子。我想将此帖子数据作为道具传递给不同的组件。

特别是,我有一个名为PostViewer的组件,它将显示帖子信息。我希望它默认显示通过props从其父级传入的帖子,否则显示通过状态调用设置的数据。

目前,我的代码的相关部分看起来像这样。

var ReactBlog = React.createClass({
  getInitialState: function() {
    return {
      posts: []
    };
  },
  componentDidMount: function() {
    $.get(this.props.url, function(data) {
      if (this.isMounted()) {
        this.setState({
          posts: data
        });
      }
    }.bind(this));
  },
  render: function() {
    var latestPost = this.state.posts[0];
    return (
      <div className="layout">
        <div className="layout layout-sidebar">
          <PostList posts={this.state.posts}/>
        </div>
        <div className="layout layout-content">
          <PostViewer post={latestPost}/>
        </div>
      </div>
    )
  }
});

和子组件:

var PostViewer = React.createClass({
  getInitialState: function() {
    return {
      post: this.props.post
    }
  },
  render: function() {
    /* handle check for initial load which doesn't include prop data yet */
    if (this.state.post) {
      return (
        <div>
          {this.state.post.title}
        </div>
      )
    }
    return (
      <div/>
    )
  }
});

如果我将孩子的if语句中的if语句和内容换成this.props,上面的工作就会起作用。*但是,这意味着我以后无法通过状态更改内容,对吗?

TLDR:我想设置一个默认帖子,通过子组件中的props(AJAX调用的结果)进行查看,我希望能够通过添加onClick事件(另一个组件)来更改正在查看的帖子)将更新状态。

这是正确的方法吗?

我应用的组件的当前层次结构是:

React Blog
 - Post List
  - Post Snippet (click will callback on React Blog and update Post Viewer)
 - Post Viewer (default post passed in via props)

谢谢!

编辑:

所以我最终做的是使用基于this.state的值在ReactBlog中附加道具。这确保了当我更改状态并在子组件中正确呈现时它会更新。但是,要做到这一点,我必须通过所有各种子组件链接onClick回调。它是否正确?看起来它可能变得非常混乱。这是我的完整示例代码:

var ReactBlog = React.createClass({
  getInitialState: function() {
    return {
      posts: [],
    };
  },
  componentDidMount: function() {
    $.get(this.props.url, function(data) {
      if (this.isMounted()) {
        this.setState({
          posts: data,
          post: data[0]
        });
      }
    }.bind(this));
  },
  focusPost: function(slug) {
    $.get('/api/posts/' + slug, function(data) {
      this.setState({
        post: data
      })
    }.bind(this));
  },
  render: function() {
    return (
      <div className="layout">
        <div className="layout layout-sidebar">
          <PostList handleTitleClick={this.focusPost} posts={this.state.posts}/>
        </div>
        <div className="layout layout-content">
          <PostViewer post={this.state.post}/>
        </div>
      </div>
    )
  }
});

var PostList = React.createClass({
  handleTitleClick: function(slug) {
    this.props.handleTitleClick(slug);
  },
  render: function() {
    var posts = this.props.posts;

    var postSnippets = posts.map(function(post, i) {
      return <PostSnippet data={post} key={i} handleTitleClick={this.handleTitleClick}/>;
    }, this);

    return (
      <div className="posts-list">
        <ul>
          {postSnippets}
        </ul>
      </div>
    )
  }
});

var PostSnippet = React.createClass({
  handleTitleClick: function(slug) {
    this.props.handleTitleClick(slug);
  },
  render: function() {
    var post = this.props.data;
    return (
      <li>
        <h1 onClick={this.handleTitleClick.bind(this, post.slug)}>{post.title}</h1>
      </li>
    )
  }
});

var PostViewer = React.createClass({
  getInitialState: function() {
    return {
      post: this.props.post
    }
  },
  render: function() {
    /* handle check for initial load which doesn't include prop data yet */
    if (this.props.post) {
      return (
        <div>
          {this.props.post.title}
        </div>
      )
    }
    return (
      <div/>
    )
  }
});

仍然希望得到一些反馈/希望这会有所帮助!

3 个答案:

答案 0 :(得分:2)

这是一个老问题,但我相信仍然相关,所以我要投入2美分。

理想情况下,您希望将任何ajax调用分离为动作文件,而不是在组件内部执行。没有使用像Redux这样的东西来帮助你管理你的状态(在这个时候,我会建议使用redux + react-redux),你可以使用一个叫做“容器组件”的东西来完成所有重型状态的提升你然后在进行主要布局的组件中使用道具。这是一个例子:

// childComponent.js
import React from 'react';
import axios from 'axios'; // ajax stuff similar to jquery but with promises

const ChildComponent = React.createClass({
  render: function() {
    <ul className="posts">
      {this.props.posts.map(function(post){
        return (
          <li>
            <h3>{post.title}</h3>
            <p>{post.content}</p>
          </li>
        )
      })}
    </ul>
  }
})

const ChildComponentContainer = React.createClass({
  getInitialState: function() {
    return {
      posts: []
    }
  },
  componentWillMount: function() {
    axios.get(this.props.url, function(resp) {
      this.setState({
        posts: resp.data
      });
    }.bind(this));
  },
  render: function() {
    return (
      <ChildComponent posts={this.state.posts} />
    )
  }
})

export default ChildComponentContainer;

答案 1 :(得分:0)

博客在很大程度上是静态的,因此您可以利用React不可变结构来“渲染一切”#34;一直而不是使用国家。

一种选择是使用路由器(如page.js)来获取数据。

以下是一些代码http://jsbin.com/qesimopugo/1/edit?html,js,output

如果你不理解某些事情,请告诉我;)

答案 2 :(得分:0)

如果您将回调传递到多个级别,请摆脱isMounted并使用context