Передача результатов AJAX в качестве реквизита для детского компонента

Я пытаюсь создать блог в React. В моем основном компоненте ReactBlog я выполняю вызов AJAX на сервере node, чтобы вернуть массив сообщений. Я хочу передать эти данные сообщения различным компонентам в качестве реквизита.

В частности, у меня есть компонент, называемый PostViewer, который будет отображать информацию о сообщении. Я хочу, чтобы он по умолчанию показывал сообщение, отправленное из его родителя через реквизиты, и в противном случае показывал данные, которые устанавливаются с помощью государственного вызова.

В настоящее время соответствующие части моего кода выглядят следующим образом.

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. * Однако это означало бы, что я не смог бы изменить содержимое позже через состояние, правильно?

TL;DR: я хочу установить пост по умолчанию для просмотра через реквизиты в дочернем компоненте (результаты вызова 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)

Спасибо!

EDIT:

Итак, что я закончил делать, это привязать реквизит в ReactBlog, используя значение, основанное на this.state. Это гарантировало, что он обновляется, когда я меняю состояние и правильно рисую дочерние компоненты. Однако для этого мне пришлось подключать callback-запросы 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/>
    )
  }
});

Все еще надеемся получить некоторые отзывы/надеюсь, что это поможет!

Ответ 1

Это старый вопрос, но я считаю, все еще актуальным, поэтому я собираюсь бросить свои 2 цента.

В идеале вы хотите выделить любые аякс-вызовы в файл действий вместо того, чтобы делать это прямо внутри компонента. Не вдаваясь в что-то вроде Redux, чтобы помочь вам управлять своим состоянием (которое в данный момент я бы рекомендовал для уменьшения x + response-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

Блог по большей части статичен, поэтому вы можете использовать React неизменяемые структуры для "рендеринга всего" все время, а не для использования состояния.

Один из вариантов заключается в использовании маршрутизатора (например, page.js) для извлечения данных.

Вот код http://jsbin.com/qesimopugo/1/edit?html,js,output

Если вы ничего не понимаете, просто дайте мне знать;)

Ответ 3

Состояние должно быть основным файлом просмотра реакции, а не для компонентов многократного использования, например здесь PostViewer, он просто отображает заголовок сообщения, если он имеет заголовок в качестве реквизита, в противном случае он будет отображаться пустым.

var PostViewer = React.createClass({

  render: function() {

    if (this.props.hasOwnProperty('title')) {
      return (
        <div>
          {this.props.title}
        </div>
      )
    }
    return (
      <div/>
    )
  }
});

Ответ 4

Избавьтесь от isMounted и используйте context, если вы передаете обратные вызовы на несколько уровней