最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Passing AJAX Results As Props to Child Component - Stack Overflow

programmeradmin1浏览0评论

I'm trying to create a blog in React. In my main ReactBlog Component, I'm doing an AJAX call to a node server to return an array of posts. I want to pass this post data to different ponents as props.

In particular, I have a ponent called PostViewer that will show post information. I want it to by default show the post passed in from its parent via props, and otherwise show data that is set via a state call.

Currently, the relevant parts of my code looks like this.

var ReactBlog = React.createClass({
  getInitialState: function() {
    return {
      posts: []
    };
  },
  ponentDidMount: 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>
    )
  }
});

and the child ponent:

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/>
    )
  }
});

The above works if I swap out the if statement and content in my child's render to this.props.* However, this would mean that I couldn't change the content later via state, correct?

TLDR: I want to set a default post to be viewed via props in a child ponent (results of an AJAX call), and I want to be able to change what post is being viewed by adding onClick events (of another ponent) that will update the state.

Is this the correct way to go about it?

Current hierarchy of my app's ponents are:

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

Thanks!

EDIT:

So what I ended up doing was attaching the props in ReactBlog using a value based on this.state. This ensured that it updates when I change state and renders correctly in child ponents. However, to do this I had to chain onClick callbacks up through all the various child ponents. Is this correct? It seems like it could get VERY messy. Here's my full example code:

var ReactBlog = React.createClass({
  getInitialState: function() {
    return {
      posts: [],
    };
  },
  ponentDidMount: 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/>
    )
  }
});

Still hoping to get some feedback / hope this helps!

I'm trying to create a blog in React. In my main ReactBlog Component, I'm doing an AJAX call to a node server to return an array of posts. I want to pass this post data to different ponents as props.

In particular, I have a ponent called PostViewer that will show post information. I want it to by default show the post passed in from its parent via props, and otherwise show data that is set via a state call.

Currently, the relevant parts of my code looks like this.

var ReactBlog = React.createClass({
  getInitialState: function() {
    return {
      posts: []
    };
  },
  ponentDidMount: 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>
    )
  }
});

and the child ponent:

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/>
    )
  }
});

The above works if I swap out the if statement and content in my child's render to this.props.* However, this would mean that I couldn't change the content later via state, correct?

TLDR: I want to set a default post to be viewed via props in a child ponent (results of an AJAX call), and I want to be able to change what post is being viewed by adding onClick events (of another ponent) that will update the state.

Is this the correct way to go about it?

Current hierarchy of my app's ponents are:

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

Thanks!

EDIT:

So what I ended up doing was attaching the props in ReactBlog using a value based on this.state. This ensured that it updates when I change state and renders correctly in child ponents. However, to do this I had to chain onClick callbacks up through all the various child ponents. Is this correct? It seems like it could get VERY messy. Here's my full example code:

var ReactBlog = React.createClass({
  getInitialState: function() {
    return {
      posts: [],
    };
  },
  ponentDidMount: 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/>
    )
  }
});

Still hoping to get some feedback / hope this helps!

Share Improve this question edited Feb 20, 2015 at 11:06 nilgun 10.6k4 gold badges48 silver badges57 bronze badges asked Feb 18, 2015 at 22:46 JonJon 6,2854 gold badges23 silver badges33 bronze badges 1
  • You are doing it all right after the edition. Using a state in a parent ponent to provide with props to the children is the way to go. Your problem was that "getInitialState" only executes once for every ponent (unless it is unmounted) so when you changed the parent state, even when the child props would change too, its state wouldn't. – GonArrivi Commented Apr 20, 2016 at 18:37
Add a ment  | 

3 Answers 3

Reset to default 2

This is an old question, but I believe still relevant, so I'm going to throw in my 2 cents.

Ideally, you want to separate out any ajax calls into an actions file instead of doing it right inside a ponent. Without going into using something like Redux to help you manage your state (which, at this point in time, I would remend redux + react-redux), you could use something called "container ponents" to do all of the heavy state lifting for you and then use props in the ponent that's doing the main layout. Here's an example:

// 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: []
    }
  },
  ponentWillMount: 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;

A blog is static for the most part, so you could exploit React immutable structures to "render everything" all the time instead of using the state.

One option for this is to use a router (like page.js) to fetch data.

Here is some code http://jsbin./qesimopugo/1/edit?html,js,output

If you don't understand something just let me know ;)

Get rid of isMounted and make use of context if you're passing callbacks down several levels

发布评论

评论列表(0)

  1. 暂无评论