Promise中的this.setState会导致奇怪的行为

Mor*_*ool 6 react-native

简化问题.在Promise中调用this.setState,在结束挂起Promise之前呈现.

我的问题是:

  1. this.setState不会立即返回
    • 我希望它是异步的,以便先挂起未决的承诺.
  2. 如果渲染函数内部出现问题,则调用Promise中的catch.
    • 也许同样的问题1)似乎渲染仍然在调用this.setState的promise的上下文中.

import dummydata_rankrequests from "../dummydata/rankrequests";
class RankRequestList extends Component {

  constructor(props) {
    super(props); 

    this.state = { loading: false, data: [], error: null };

    this.makeRankRequestCall = this.makeRankRequestCall.bind(this);
    this.renderItem = this.renderItem.bind(this);
  }

  componentDidMount() {

    // WORKS AS EXPECTED
    // console.log('START set');
    // this.setState({ data: dummydata_rankrequests.data, loading: false });
    // console.log('END set');

    this.makeRankRequestCall()
    .then(done => {
      // NEVER HERE
      console.log("done");
    });    
  }

  makeRankRequestCall() {
    console.log('call makeRankRequestCall');
    try {
      return new Promise((resolve, reject) => {
        resolve(dummydata_rankrequests);
      })
      .then(rankrequests => {
        console.log('START makeRankRequestCall-rankrequests', rankrequests);
        this.setState({ data: rankrequests.data, loading: false });
        console.log('END _makeRankRequestCall-rankrequests');
        return null;
      })
      .catch(error => {
        console.log('_makeRankRequestCall-promisecatch', error);
        this.setState({ error: RRError.getRRError(error), loading: false });
      });
    } catch (error) {
      console.log('_makeRankRequestCall-catch', error);
      this.setState({ error: RRError.getRRError(error), loading: false });
    }
  }

  renderItem(data) {
    const height = 200;
    // Force a Unknown named module error here
    return (
      <View style={[styles.item, {height: height}]}>
      </View>
    );
  }

  render() {
    let data = [];
    if (this.state.data && this.state.data.length > 0) {
      data = this.state.data.map(rr => {
        return Object.assign({}, rr);
      });
    }
    console.log('render-data', data);
    return (
      <View style={styles.container}>
        <FlatList style={styles.listContainer1}
          data={data}
          renderItem={this.renderItem}
        />
      </View>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

Currrent日志显示:

  • 渲染数据,[]
  • START makeRankRequestCall-rankrequests
  • 渲染数据,[...]
  • _makeRankRequestCall-promisecatch错误:未知的命名模块...
  • 渲染数据,[...]
  • 可能未处理的承诺

Android模拟器"反应":"16.0.0-alpha.12","react-native":"0.46.4",

编辑:围绕this.setState包装setTimeout也可以

    setTimeout(() => {
      this.setState({ data: respData.data, loading: false });
    }, 1000);
Run Code Online (Sandbox Code Playgroud)

EDIT2:并行地在react-native github中创建了一个错误报告 https://github.com/facebook/react-native/issues/15214

Dan*_*ash 1

和在 JavaScript 中Promise都是this.setState()异步的。假设您有以下代码:

console.log(a);
networkRequest().then(result => console.log(result)); // networkRequest() is a promise
console.log(b);
Run Code Online (Sandbox Code Playgroud)

a 和 b 将首先打印,然后是网络请求的结果。

同样,this.setState()也是异步的,所以如果你想在完成后执行某些操作this.setState(),你需要这样做:

this.setState({data: rankrequests.data}, () => {
  // Your code that needs to run after changing state
})
Run Code Online (Sandbox Code Playgroud)

React 每次this.setState()执行时都会重新渲染,因此您可以在整个承诺得到解决之前更新组件。这个问题可以通过创建componentDidMount()as 异步函数并使用 wait 来解决承诺来解决:

async componentDidMount() {
  let rankrequests;
  try {
    rankrequests = await this.makeRankRequestCall() // result contains your data
  } catch(error) {
    console.error(error);
  }
  this.setState({ data: rankrequests.data, loading: false }, () => {
    // anything you need to run after setting state
  });
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你。