考虑以下几乎相同的两个片段。
区别是:
- 第一个使用
setTimeout()
触发事件 - 当按钮单击时,第二个触发事件
如果您检查控制台,您会发现摘要1中的最后两行是:
App rendering 1 folder(s)
Observed js
和摘要中的2是:
Observed js
App rendering 1 folder(s)
问题:为什么订单反转?
settimeout()游乐场
按钮游乐场
摘要1:settimeout()触发
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>
摘要2:按钮触发
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>
它们以不同的顺序运行,因为React尝试将setState()
拨打在一起,因此调用setState()
不会导致组件同步重新渲染,而是等待事件回调返回。
但是,只有当您对setState
的呼叫是反应驱动事件的结果时,才这样做,例如onClick
是。当您使用setTimeout
时,React(当前)无法知道您何时完成,因此无法将它们批量批量。相反,它同步重新呈现。
最好,我可以告诉文档,仅在传递中间接提及此行为:
setState()不会立即突变。 等待国家过渡。访问此。 方法可能会返回现有值。
无法保证呼叫setState的同步操作 呼叫可能会批次提高。
https://facebook.github.io/react/docs/react-component.html#settate
如果您想对批处理事物做出反应,则需要将您的回调代码包装在ReactDOM.unstable_batchedUpdates
中,顾名思义,这不是稳定的API,因此它可以(并且很可能)在不警告的情况下进行更改。
setTimeout(() => {
ReactDOM.unstable_batchedUpdates(() => {
console.log('Emitting event');
this.events$.next({
type: 'ADD_FOLDER',
name: 'js',
permissions: 400
});
});
}, 1000);
理想情况下,您的代码将以无关紧要的顺序构造。