gpt4 book ai didi

reactjs - 为什么按钮点击触发与setTimeout()触发不同?

转载 作者:行者123 更新时间:2023-12-03 14:14:24 26 4
gpt4 key购买 nike

考虑以下几乎相同的两个片段。

区别在于:

  • 第一个使用 setTimeout() 触发事件
  • 第二个在单击按钮时触发事件

如果您检查控制台,您将看到代码段 1 中的最后两行是:

App rendering 1 folder(s)
Observed js

在代码片段 2 中是:

Observed js
App rendering 1 folder(s)

问题:为什么顺序颠倒了?

setTimeout() playground

Button playground

<小时/>

代码片段 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 的调用是 React 驱动事件的结果(例如 onClick)时,它才会执行此操作。当您使用 setTimeout 时,React(当前)无法知道您何时完成,因此无法将它们批处理在一起。相反,它会立即同步重新渲染。

据我所知,React 文档只是间接提及了这种行为:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

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);

理想情况下,您的代码的结构方式与顺序无关。

关于reactjs - 为什么按钮点击触发与setTimeout()触发不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42659326/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com