考虑以下几乎完全相同的两个片段。
区别在于:
setTimeout()
来触发事件如果您检查控制台,您会看到代码段1中的最后两行是:
App rendering 1 folder(s)
Observed js
并在代码段2中:
Observed js
App rendering 1 folder(s)
问题:为什么订单被撤销了?
class App extends React.Component {
constructor() {
super();
this.events$ = new Rx.Subject();
this.eventsByName$ = this.events$.groupBy(e => e.name);
this.state = {};
setTimeout(() => {
console.log('Emitting event');
this.events$.next({
type: 'ADD_FOLDER',
name: 'js',
permissions: 400
});
}, 1000);
}
componentDidMount() {
this.eventsByName$.subscribe(folderEvents$ => {
const folder = folderEvents$.key;
console.log(`New stream for "${folder}" created`);
folderEvents$.subscribe(e => {
console.log(`Observed ${e.name}`);
});
this.setState({
[folder]: folderEvents$
});
});
}
render() {
const folders = Object.keys(this.state);
console.log(`App rendering ${folders.length} folder(s)`);
return (
<div>
{
folders.map(folder => (
<div key={folder}>
{folder}
</div>
))
}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<head>
<script src="https://unpkg.com/rxjs@5.2.0/bundles/Rx.js"></script>
<script src="https://unpkg.com/react@15.4.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.4.2/dist/react-dom.js"></script>
</head>
<body>
<div id="app"></div>
</body>
class App extends React.Component {
constructor() {
super();
this.events$ = new Rx.Subject();
this.eventsByName$ = this.events$.groupBy(e => e.name);
this.state = {};
}
componentDidMount() {
this.eventsByName$.subscribe(folderEvents$ => {
const folder = folderEvents$.key;
console.log(`New stream for "${folder}" created`);
folderEvents$.subscribe(e => {
console.log(`Observed ${e.name}`);
});
this.setState({
[folder]: folderEvents$
});
});
}
onClick = () => {
console.log('Emitting event');
this.events$.next({
type: 'ADD_FOLDER',
name: 'js',
permissions: 400
});
};
render() {
const folders = Object.keys(this.state);
console.log(`App rendering ${folders.length} folder(s)`);
return (
<div>
<button onClick={this.onClick}>
Add event
</button>
<div>
{
folders.map(folder => (
<div key={folder}>
{folder}
</div>
))
}
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<head>
<script src="https://unpkg.com/rxjs@5.2.0/bundles/Rx.js"></script>
<script src="https://unpkg.com/react@15.4.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.4.2/dist/react-dom.js"></script>
</head>
<body>
<div id="app"></div>
</body>
答案 0 :(得分:3)
它们以不同的顺序运行,因为React尝试一起批处理setState()
,因此调用setState()
不会导致组件同步重新呈现,而是等待事件回调返回。
但是,当且仅当您对setState
的调用是由onClick
引起的React驱动事件的结果时才会执行此操作。当您使用setTimeout
时,React(当前)无法知道您何时完成,因此无法将它们一起批处理。相反,它会同时重新渲染。
我能说的最好,React docs仅在传递中间接提到这种行为:
setState()不会立即改变this.state但会创建一个 待定状态转换。调用后访问this.state 方法可以返回现有值。
无法保证调用setState的同步操作 并且可以批量调用以获得性能提升。
https://facebook.github.io/react/docs/react-component.html#setstate
如果你想让React批量处理,你需要将你的回调代码包装在ReactDOM.unstable_batchedUpdates
中,顾名思义它不是一个稳定的API,所以它可以(并且可能会)在没有警告的情况下改变。
setTimeout(() => {
ReactDOM.unstable_batchedUpdates(() => {
console.log('Emitting event');
this.events$.next({
type: 'ADD_FOLDER',
name: 'js',
permissions: 400
});
});
}, 1000);
理想情况下,您的代码的结构将与订单无关紧要。