在React.js中的组件之间传递Ajax /服务数据

时间:2016-08-07 17:56:42

标签: javascript ajax reactjs

我正在尝试传递在React应用程序的一个组件中收到的数据。成功后,我将获取接收的数据并设置状态,然后尝试将该数据作为下一个组件的属性传递。进入第二个组件后,我需要通过this.state访问传递的数据,以便稍后我可以更改该组件中的状态。在从服务接收数据之前,我似乎遇到了DOM呈现的问题。我已经尝试在this.state.data中传递已经加载的值数组来代替<List data={this.state.data}/>,它似乎执行得很好。如何确保在呈现DOM之前我已从服务接收到数据,以便将数据一直传递到每​​个组件。

编辑:添加了List元素的完整实现,所以解释一下this.state的使用

这基本上就是我想做的事情:

var Box = React.createClass({

  getInitialState: function() {
    return {data: []};
  },

  loadTodosFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'GET',
      cache: false,
      success: function(dataResponse) {
        this.setState({data: dataResponse});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },

  componentDidMount: function() {
    this.loadFromServer();
  },

  render: function() {
    return (<List data={this.state.data}/>);
  }
});


var List = React.createClass({

  getInitialState: function() {
    return {data: this.props.data};
  },

dragStart: function(e) {
  this.dragged = e.currentTarget;
  e.dataTransfer.effectAllowed = 'move';
  // Firefox requires dataTransfer data to be set
  e.dataTransfer.setData("text/html", e.currentTarget);
},

dragEnd: function(e) {
  this.dragged.style.display = "block";
  this.dragged.parentNode.removeChild(placeholder);
  // Update data
  var data = this.state.data;
  var from = Number(this.dragged.dataset.id);
  var to = Number(this.over.dataset.id);
  if(from < to) to--;
  if(this.nodePlacement == "after") to++;
  data.splice(to, 0, data.splice(from, 1)[0]);
  this.setState({data: data});
},

dragOver: function(e) {
  e.preventDefault();
  this.dragged.style.display = "none";
  if(e.target.className == "placeholder") return;
  this.over = e.target;
  // Inside the dragOver method
  var relY = e.clientY - this.over.offsetTop;
  var height = this.over.offsetHeight / 2;
  var parent = e.target.parentNode;

  if(relY > height) {
    this.nodePlacement = "after";
    parent.insertBefore(placeholder, e.target.nextElementSibling);
  }
  else if(relY < height) {
    this.nodePlacement = "before"
    parent.insertBefore(placeholder, e.target);
  }
},

  render: function() {
    var results = this.state.data;
      return (
        <ul>
            {
              results.map(function(result, i) {
                return (
                  <li key={i}>{result}</li>
                )
              })
            }
        </ul>
      );
    }
  });


ReactDOM.render(
  <Box url="/api/comments"/>, document.getElementById('content')
);

1 个答案:

答案 0 :(得分:2)

组件加载后数据加载未呈现数据的原因是List.render函数中的这一行:

var results = this.state.data;

基本上,您已经制作了原始道具的副本,并使用getInitialState方法将它们分配给List组件中的状态。之后你的状态和道具脱钩了。这意味着如果props.data在List组件上发生更改,则状态不会知道它,因此不会重新呈现任何内容。

因此,不使用state来初始化结果变量,而是使用props。

var results = this.props.data

以下是它的样子:

var List = React.createClass({

  render: function() {
    var results = this.props.data;
      return (
        <ul>
            {
              results.map(function(result, i) {
                return (
                  <li key={i}>{result}</li>
                )
              })
            }
        </ul>
      );
    }
  });

现在,只要数据发生变化,就会更新道具并最终重新渲染结果。

已更新,以解决OP的评论:

如果您想更新列表的状态但希望每次父级道具更改时都收到通知,那么您希望使用方法componentWillReceiveProps,以便在获取数据时,子列表是收到通知。在这种方法中,您可以设置新状态:

componentWillReceiveProps: function(newProps) {
    this.setState({data: this.props.data});
} 

执行此操作后,react会为您重新呈现列表。

另一个更新:为了说明这是如何工作的,我将一个例子here放在一起。

这里有JS代码:

let todos = ["Run","Swim","Skate"];

class MyList extends React.Component{
  componentWillMount() {
    console.log("Props are: ", this.props);
    this.setState({list: this.props.items});
  }

  componentWillReceiveProps(newProps) {
    console.log("Received Props are: ", newProps);
    this.setState({list: newProps.items});
  }

  render() {
    return (<ul>
            {this.state.list.map((todo) => <li>{todo}</li>)}
            </ul>);
  }
}

class App extends React.Component{
  constructor() {
      super();
      console.log("State is: ", this.state);
  }

  componentWillMount() {
    this.setState({items: ["Fly"]});
  }

  componentDidMount() {
    setTimeout(function(){
      console.log("After 2 secs");
      this.setState({items: todos});
    }.bind(this), 2000);
  }

  render() {
      return (<MyList items={this.state.items}/>);
  }
}


ReactDOM.render(<App/>, document.getElementById("app"));