在React

时间:2017-12-13 21:46:50

标签: javascript reactjs promise

搜索这个问题总会带来一些基本归结为

的东西
class Test extends Component {
  componentDidMount() {
    this.props.promise.then(value => this.setState({value}));
  }
  render() {
    return <div>{this.state.value || "Loading"}</div>;
  }
}

如果(1)组件不会很快卸载,并且(2)承诺永远不会改变,这是有效的。当试图提出更通用的解决方案时,这两个都会产生潜在的问题。

第一个问题可以通过添加&#34; guard&#34;来解决。财产,没什么大不了的。

在尝试修复第二个问题时,很容易遇到竞争条件。请考虑以下代码:

&#13;
&#13;
const timedPromise = (value, delay) => new Promise((resolve, reject) => {
  setTimeout(() => resolve(value), delay);
});

class Test extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: "Loading..."};
  }
  
  componentWillMount() {
    this.setPromise(timedPromise("Hello 1", 3000));
    setTimeout(() => {
      this.setPromise(timedPromise("Hello 2", 1000));
    }, 1000);
  }

  setPromise(promise) {
    promise.then(value => this.setState({value}));
  }

  render() {
    return <div>{this.state.value}</div>;
  }
}

ReactDOM.render(<Test/>, document.getElementById("root"));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
&#13;
&#13;
&#13;

在这个例子中,&#34; Hello 1&#34;最终会显示,即使&#34; Hello 2&#34;请求后来发起。在某些接口中,这是一个非常常见的问题,用户发出多个请求并最终导致状态不正确。

我能想出的最佳解决方案如下:

&#13;
&#13;
const timedPromise = (value, delay) => new Promise((resolve, reject) => {
  setTimeout(() => resolve(value), delay);
});

class Test extends React.Component {
  constructor(props) {
    super(props)
    this.state = {data: {value: "Waiting..."}};
  }

  componentWillMount() {
    this.setPromise(timedPromise("Hello 1", 3000));
    setTimeout(() => {
      this.setPromise(timedPromise("Hello 2", 1000));
    }, 1000);
  }

  setPromise(promise) {
    const data = {value: "Loading..."};
    this.setState({data: data})
    promise.then(value => {
      data.value = value;
      this.forceUpdate();
    })
  }

  render() {
    return <div>{this.state.data.value}</div>;
  }
}

ReactDOM.render(<Test/>, document.getElementById("root"));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
&#13;
&#13;
&#13;

这样可行,但它依赖于forceUpdate,这看起来并不优雅。我似乎无法通过在不会遇到其他竞争条件的状态下存储某些值来找到解决方法,因为规范不保证setState分辨率的顺序。

是否有更好的模式来解决这个看似常见的问题?

1 个答案:

答案 0 :(得分:1)

您可以通过跟踪指针来解决此问题,并且只有在已解析的指针与当前指针(然后是最新的指针)匹配时才保存状态。否则不要理会结果。

这样设置,这不是保持React状态的最佳方法。毕竟它是一个视图库。可能是Redux可以帮助你(或者它可能太多了,这也可能,真的取决于用例)。

如果您想在React中保留所有内容,可以使用此类内容。

class Example extends Component {
  constructor(props) {
    super(props);
    this.state({value: 'Loading', currentPointer: 0});
  }

  componentDidMount() {
    this.setPromise(timedPromise("Hello 1", 3000));
    setTimeout(() => {
      this.setPromise(timedPromise("Hello 2", 1000));
    }, 1000);
  }

  setPromise(promise) {
    const newPointer = this.state.currentPointer + 1;
    this.setState({currentPointer: newPointer}, () => 
      promise.then(value => {
        if (this.state.currentPointer == newPointer) {
          this.setState({value});
        } else {
          console.log(`Pointer mismatch, got ${newPointer} but current was ${this.state.currentPointer}`);
        }
      })
    });
  }

  render() {
    return <div>{this.state.data.value}</div>;
  }
}