简化问题。在Promise中调用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>
);
}
}
Currrent日志显示:
Android模拟器 &#34;反应&#34;:&#34; 16.0.0-alpha.12&#34;, &#34; react-native&#34;:&#34; 0.46.4&#34;,
修改: 在this.setState周围包装setTimeout也可以正常工作
setTimeout(() => {
this.setState({ data: respData.data, loading: false });
}, 1000);
EDIT2 : 在react-native github中并行创建了一个bug报告 https://github.com/facebook/react-native/issues/15214
答案 0 :(得分:1)
Promise
和this.setState()
在javascript中都是异步的。比如,如果您有以下代码:
console.log(a);
networkRequest().then(result => console.log(result)); // networkRequest() is a promise
console.log(b);
首先打印a和b,然后打印网络请求的结果。
同样,this.setState()
也是异步的,因此,如果您想在this.setState()
完成后执行某些操作,则需要执行以下操作:
this.setState({data: rankrequests.data}, () => {
// Your code that needs to run after changing state
})
每次执行this.setState()
时React重新渲染,因此在整个承诺得到解决之前,您将更新组件。通过将componentDidMount()
作为异步函数并使用await来解决承诺,可以解决此问题:
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
});
}
希望它有所帮助。
答案 1 :(得分:1)
我也很难理解你在这里尝试做什么,所以我抓了一把。
由于this.setState()
方法旨在触发渲染,因此在准备渲染之前我不会调用它。您似乎在很大程度上依赖于状态变量是最新的并且能够随意使用/操作。此处的this.state.
变量的预期行为将在渲染时准备就绪。我认为你需要使用另一个与状态和渲染无关的更多可变变量。当你完成时,只有那时,你应该渲染。
以下是您重新编写代码的代码:
从“../ dummydata / rankrequests”导入dummydata_rankrequests;
class RankRequestList扩展了Component {
constructor(props) {
super(props);
/*
Maybe here is a good place to model incoming data the first time?
Then you can use that data format throughout and remove the heavier modelling
in the render function below
if (this.state.data && this.state.data.length > 0) {
data = this.state.data.map(rr => {
return Object.assign({}, rr);
});
}
*/
this.state = {
error: null,
loading: false,
data: (dummydata_rankrequests || []),
};
//binding to 'this' context here is unnecessary
//this.makeRankRequestCall = this.makeRankRequestCall.bind(this);
//this.renderItem = this.renderItem.bind(this);
}
componentDidMount() {
// this.setState({ data: dummydata_rankrequests.data, loading: false });
//Context of 'this' is already present in this lifecycle component
this.makeRankRequestCall(this.state.data).then(returnedData => {
//This would have no reason to be HERE before, you were not returning anything to get here
//Also,
//should try not to use double quotes "" in Javascript
//Now it doesn't matter WHEN we call the render because all functionality had been returned and waited for
this.setState({ data: returnedData, loading: false });
}).catch(error => {
console.log('_makeRankRequestCall-promisecatch', error);
this.setState({ error: RRError.getRRError(error), loading: false });
});
}
//I am unsure why you need a bigger call here because the import statement reads a JSON obj in without ASync wait time
//...but just incase you need it...
async makeRankRequestCall(currentData) {
try {
return new Promise((resolve, reject) => {
resolve(dummydata_rankrequests);
}).then(rankrequests => {
return Promise.resolve(rankrequests);
}).catch(error => {
return Promise.reject(error);
});
} catch (error) {
return Promise.reject(error);
}
}
renderItem(data) {
const height = 200;
//This is usually where you would want to use your data set
return (
<View style={[styles.item, {height: height}]} />
);
/*
//Like this
return {
<View style={[styles.item, {height: height}]}>
{ data.item.somedataTitleOrSomething }
</View>
};
*/
}
render() {
let data = [];
//This modelling of data on every render will cause a huge amount of heaviness and is not scalable
//Ideally things are already modelled here and you are just using this.state.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
data={data}
style={styles.listContainer1}
renderItem={this.renderItem.bind(this)} />
{ /* Much more appropriate place to bind 'this' context than above */ }
</View>
);
}
}
答案 2 :(得分:1)
setState
确实是异步的。我想makeRankRequestCall
应该是这样的:
async makeRankRequestCall() {
console.log('call makeRankRequestCall');
try {
const rankrequests = await new Promise((resolve, reject) => {
resolve(dummydata_rankrequests);
});
console.log('START makeRankRequestCall-rankrequests', rankrequests);
this.setState({ data: rankrequests.data, loading: false });
console.log('END _makeRankRequestCall-rankrequests');
} catch(error) {
console.log('_makeRankRequestCall-catch', error);
this.setState({ error: RRError.getRRError(error), loading: false });
}
}
其次,承诺发现renderItem
的错误完全没问题。在JavaScript中,任何catch块都将捕获代码中任何位置抛出的任何错误。根据{{3}}:
throw语句抛出用户定义的异常。当前函数的执行将停止(抛出后的语句将被执行),并且控制将被传递给调用堆栈中的第一个catch块。如果调用函数之间不存在catch块,则程序将终止。
因此,为了解决问题,如果您希望renderItem
失败,可以执行以下操作:
renderItem(data) {
const height = 200;
let item = 'some_default_item';
try {
// Force a Unknown named module error here
item = styles.item
} catch(err) {
console.log(err);
}
return (
<View style={[item, {height: height}]}>
</View>
);
}