Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript ReactJS:当组件状态为';它是基于AJAX的吗?_Javascript_Reactjs - Fatal编程技术网

Javascript ReactJS:当组件状态为';它是基于AJAX的吗?

Javascript ReactJS:当组件状态为';它是基于AJAX的吗?,javascript,reactjs,Javascript,Reactjs,摘要:ReactJS新手,我正在尝试找出更新组件的最佳方法,因为组件的状态取决于远程API(即通过AJAX API保持组件状态与远程数据库同步) 示例用例:设想一个产品库存,单击按钮将产品添加到购物车中,并将库存减少1。每次用户单击该组件时,它都会启动一个AJAX请求,然后在请求完成后,该组件通过调用setState()重新呈现新产品清单 问题:我遇到了一个问题,因为setState()和AJAX请求都是异步的,所以组件与服务器不同步。例如,如果您非常快速地单击,您可以为单个产品ID启动多个AJ

摘要:ReactJS新手,我正在尝试找出更新组件的最佳方法,因为组件的状态取决于远程API(即通过AJAX API保持组件状态与远程数据库同步)

示例用例:设想一个产品库存,单击按钮将产品添加到购物车中,并将库存减少1。每次用户单击该组件时,它都会启动一个AJAX请求,然后在请求完成后,该组件通过调用
setState()
重新呈现新产品清单

问题:我遇到了一个问题,因为
setState()
和AJAX请求都是异步的,所以组件与服务器不同步。例如,如果您非常快速地单击,您可以为单个产品ID启动多个AJAX请求,因为组件的状态尚未更新以反映产品ID不再在库存中。下面我举一个简单的例子来说明这个概念:

解决方案不充分:如果客户端请求不再在库存中的产品,则可以通过向服务器端发送错误来处理此问题,然而,我确实在寻找在客户端处理ReactJS中常见场景的最佳方法,并确保我了解处理组件状态的最佳方法

Component extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      clicksLeft: 0,
    };
  }
  componentDidMount() {
    //getClicksLeft is async and takes a callback, think axios/superagent
    getClicksLeft((response) => { 
      this.setState(response);
    });
  }
  btnClicked = () => {
    //This may appear redundant/useless but 
    //imagine sending an element in a list and then requesting the updated 
    //list back
    const data = {clicks: this.state.clicksLeft--};
    decrementClicksLeft(data, () => {
      getClicksLeft((response) => { 
        this.setState(response);
      });     
    }
  }
  render() {
    <button onClick={this.btnClicked}>Click me {this.state.clicksLeft} times</button>
  }
}
组件扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
单击左键:0,
};
}
componentDidMount(){
//getClicksLeft是异步的,并接受回调,比如axios/superagent
getClicksLeft((响应)=>{
此.setState(响应);
});
}
btnClicked=()=>{
//这可能显得多余/无用,但是
//想象一下,发送列表中的一个元素,然后请求更新的
//列回
const data={clicks:this.state.clicksLeft-->;
递减ICKSLEFT(数据,()=>{
getClicksLeft((响应)=>{
此.setState(响应);
});     
}
}
render(){
单击我{this.state.clicksLeft}次
}
}

当单击按钮时,是否有任何理由必须调用
getClicksLeft
?在安装组件时,您已经调用了它,然后只要单击按钮,您就可以将该数字减少1

btnClicked = () => {
  if (this.state.clicksLeft > 0) {
    decrementClicksLeft();    
    this.setState({clicksLeft: this.state.clicksLeft - 1});
  }
}
如果一次只有一个用户想买东西,这就行了。否则你也可以在购买之前检查剩余的数量

btnClicked = () => {
  getClicksLeft((response) => { 
    if (response > 0) {
      decrementClicksLeft();
      this.setState({clicksLeft: this.state.clicksLeft - 1});
    }
  });     
}

这样,如果没有点击,什么也不会发生。

最基本的解决方案是在等待响应返回时禁用按钮:

(我还简化了代码。)

组件扩展了React.Component{
建造师(道具){
超级(道具);
//初始状态
此.state={
clicksLeft:0,//没有可用的单击
makeRequest:false,//我们没有要求向服务器发出请求
pendingTransaction:false,//当前没有向服务器发出请求
};
}
componentDidMount(){
//初始加载已完成,请获取点击次数
这是。_getClicksRemaining();
}
//当道具/状态改变时调用
//未为初始渲染调用
组件将更新(下一步,下一步状态){
//如果没有向服务器发出现有请求,并且如果
//州政府要求我们提出请求(如handleButtonClick中所述)
//然后去提出请求
if(!this.state.pendingTransaction&&nextState.makeRequest){
常数数据={
单击:this.state.clicksLeft--,
};
//递减ICKSLEFT是异步的
递减icksleft(数据,()=>this._getClicksRemaining());
//首先触发上面的异步递减ICKSLEFT请求,然后
//告诉组件有一个挂起的请求,并且
//不允许尝试提出新请求
//注意,这是代码中易受攻击的部分
//最初的问题,理论上用户可以单击按钮
//在此设置状态完成之前再次执行。但是,如果您的用户能够
//要做到这一点,他们可能正在使用脚本,而你不应该这样做
//担心它们。设置状态/渲染速度非常快,所以这应该是
//足够的保护,防止人为点击
这是我的国家({
makeRequest:false,
pendingTransaction:对,
});
}
}
_getClicksRemaining(){
//getClicksLeft是异步的
getClicksLeft((响应)=>{
//这里我们在getClicksLeft的回调中,因此
//知道它已经完成。所以,重置我们的标志以显示它
//没有尚未处理的请求
const newState=Object.assign(
{
pendingTransaction:错误,
},
答复,,
);
this.setState(newState);
}); 
}
//按钮被点击了
_把手按钮点击=()=>{
如果(!this.state.pendingTransaction){
//如果当前没有向服务器发出请求,则可以安全地
//创建一个新的。在此处设置状态将导致'componentWillUpdate'`
//被叫
这是我的国家({
makeRequest:true,
});
}
}
render(){
//如果出现以下情况,请禁用该按钮:
//*没有点击
//*服务器有一个挂起的请求
const buttonDisabled=((this.state.clicksLeft==0)| | this.state.pendingTransaction);
返回(
单击我{this.state.clicksLeft}次
);
}
}

在花了一段时间使用react-redux、redux-thunk和redux-pack之后,我决定使用更简单的方法:。我并不真正需要redux的复杂性,因为我只是在列表上执行post和get操作。我还需要一些简单的副作用,比如当我发布一篇文章时,我需要更新多个列表(这是通过
实现的,然后
Component extends React.Component {
  constructor(props) {
    super(props);

    // Initial state
    this.state = {
      clicksLeft: 0, // No clicks are availabe
      makeRequest: false, // We are not asking to make a request to the server
      pendingTransaction: false, // There is no current request out to the server
    };
  }

  componentDidMount() {
    // Initial load completed, so go get the number of clicks
    this._getClicksRemaining();
  }

  // Called whenever props/state change
  // NOT called for the initial render
  componentWillUpdate(nextProps, nextState) {
    // If there is no existing request out to the server, AND if the next
    // state is asking us to make a request (as set in _handleButtonClick)
    // then go make the request
    if (!this.state.pendingTransaction && nextState.makeRequest) {
      const data = {
        clicks: this.state.clicksLeft--,
      };

      // decrementClicksLeft is async
      decrementClicksLeft(data, () => this._getClicksRemaining());

      // First fire off the async decrementClicksLeft request above, then
      // tell the component that there is a pending request out, and that it
      // is not allowed to try and make new requests
      // NOTE this is the one part of the code that is vulnerable to your
      // initial problem, where in theory a user could click the button
      // again before this setState completes. However, if your user is able
      // to do that, then they are probably using a script and you shouldn't
      // worry about them. setState/render is very fast, so this should be
      // more than enough protection against human clicking
      this.setState({
        makeRequest: false,
        pendingTransaction: true,
      });
    }
  }

  _getClicksRemaining() {
    // getClicksLeft is async
    getClicksLeft((response) => { 
      // Here we are inside of the callback from getClicksLeft, so we 
      // know that it has completed. So, reset our flags to show that 
      // there is no request still pending
      const newState = Object.assign(
        {
          pendingTransaction: false,
        },
        response,
      );

      this.setState(newState);
    }); 
  }

  // The button was clicked
  _handleButtonClick = () => {
    if (!this.state.pendingTransaction) {
      // If there isn't a request out to the server currently, it's safe to
      // make a new one. Setting state here will cause `componentWillUpdate`
      // to get called
      this.setState({
        makeRequest: true,
      });
    }
  }

  render() {
    // Disable the button if:
    //   * there are no clicks left
    //   * there is a pending request out to the server
    const buttonDisabled = ((this.state.clicksLeft === 0) || this.state.pendingTransaction);

    return (
      <button
        disabled={buttonDisabled}
        onClick={this._handleButtonClick}
      >
        Click me {this.state.clicksLeft} times
      </button>
    );
  }
}