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

Posted

技术标签:

【中文标题】将 AJAX 结果作为道具传递给子组件【英文标题】:Passing AJAX Results As Props to Child Component 【发布时间】:2015-04-20 03:38:57 【问题描述】:

我正在尝试在 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 语句和内容换成 this.props,则上述方法有效。* 但是,这意味着我以后无法通过状态更改内容,对吗?

TLDR:我想设置一个默认帖子以通过子组件中的道具查看(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/>
    )
  
);

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

【问题讨论】:

你在编辑后就可以了。使用父组件中的状态为子组件提供道具是要走的路。你的问题是“getInitialState”只对每个组件执行一次(除非它被卸载)所以当你改变父状态时,即使子道具也会改变,它的状态不会。 【参考方案1】:

这是一个老问题,但我认为仍然相关,所以我要投入 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;

【讨论】:

这绝对是个好方法:)【参考方案2】:

如果要将回调向下传递几个级别,请摆脱 isMounted 并使用 context

【讨论】:

【参考方案3】:

博客在很大程度上是静态的,因此您可以利用 React 不可变结构来一直“渲染所有内容”,而不是使用状态。

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

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

如果你有不明白的地方,请告诉我;)

【讨论】:

以上是关于将 AJAX 结果作为道具传递给子组件的主要内容,如果未能解决你的问题,请参考以下文章

如何将函数作为道具传递给子组件并在Vue中从那里调用它?

VUE/NUXT:将随机数作为道具传递给子组件

如何使用 Vue.js 路由器将状态和事件处理程序作为道具传递给子组件?

传递给子组件时道具未定义(反应钩子)

无法将道具从父组件传递给子组件

将“导航”和“道具”传递给子组件