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

Passing Ajax/service data between components in React.js

本文关键字:Ajax 服务 数据 之间 js React 组件      更新时间:2023-09-26

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

EDIT:添加了List元素的完整实现,因此解释了这个.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')
);

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

var results = this.state.data;

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

因此,不要使用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方法,以便在获取数据时通知子级list。在这种方法中,你可以设置新的状态:

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

完成此操作后,react将为您重新渲染列表。

另一个更新:为了说明这是如何工作的,我在这里举了一个例子。

下面是这个的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"));