redux 中的节流调度产生奇怪的行为
Throttling dispatch in redux producing strange behaviour
我有这个 class:
export default class Search extends Component {
throttle(fn, threshhold, scope) {
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
}
}
render() {
return (
<div>
<input type='text' ref='input' onChange={this.throttle(this.handleSearch,3000,this)} />
</div>
)
}
handleSearch(e) {
let text = this.refs.input.value;
this.someFunc();
//this.props.onSearch(text)
}
someFunc() {
console.log('hi')
}
}
所有这些代码都会每 3 秒注销一次 hi
- throttle
包装 handleSearch
方法的调用会处理这个
一旦我取消注释此行:
this.props.onSearch(text)
节流方法不再起作用,每次按下按键时控制台都会注销 hi
而不会暂停,并且还会调用 oSearch
函数。
这个onSearch
方法是主应用传下来的prop方法:
<Search onSearch={ text => dispatch(search(text)) } />
redux dispatch 触发了一个 redux 搜索操作,如下所示:
export function searchPerformed(search) {
return {
type: SEARCH_PERFORMED
}
}
我不知道为什么会发生这种情况 - 我猜这与 redux 有关,因为问题发生在 handleSearch 调用 onSearch 时,它又会在父组件中触发 redux 调度。
问题是第一次执行的时候就走到了else,调用了dispatch函数。 reducer 可能会立即更新一些状态,并导致重新渲染;重新渲染导致再次创建输入,新的 'throttle closure' 再次具有 null 'last' 和 'deferTimer' -> 每次都转到 else,因此立即更新。
感谢 luanped 帮我解决了这个问题。有了这个理解,我就能找到一个简单的解决方案。搜索组件不需要更新,因为输入是不受控制的组件。为了阻止我遇到的周期性问题,我使用了 shouldComponentUpdate 来防止它重新渲染:
constructor() {
super();
this.handleSearch = _.throttle(this.handleSearch,1000);
}
shouldComponentUpdate() {
return false;
}
我还将油门移到了构造函数中,因此油门只能有一个实例。
我认为这是一个很好的解决方案,但是我才刚刚开始学习 React,所以如果有人可以指出这种方法的问题,我们将不胜感激。
正如 Mike 指出的那样,如果组件不需要更新,那么不更新组件就可以获得正确的行为。
在我的例子中,我有一个组件需要每隔几秒轮询一次服务器更新,直到某些状态派生的 prop 更改值(例如 'pending' 与 'complete')。
每次有新数据进来,组件都会重新渲染,并再次调用 action creator,限制 action creator 没有用。
我可以简单地通过将相关的动作创建者交给组件安装上的 setInterval 来解决。是的,这是在渲染上发生的副作用,但很容易推理,实际的状态变化仍然通过调度程序。
如果您想保持纯粹,或者您的用例更复杂,请查看 https://github.com/pirosikick/redux-throttle-actions。
我有这个 class:
export default class Search extends Component {
throttle(fn, threshhold, scope) {
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
}
}
render() {
return (
<div>
<input type='text' ref='input' onChange={this.throttle(this.handleSearch,3000,this)} />
</div>
)
}
handleSearch(e) {
let text = this.refs.input.value;
this.someFunc();
//this.props.onSearch(text)
}
someFunc() {
console.log('hi')
}
}
所有这些代码都会每 3 秒注销一次 hi
- throttle
包装 handleSearch
方法的调用会处理这个
一旦我取消注释此行:
this.props.onSearch(text)
节流方法不再起作用,每次按下按键时控制台都会注销 hi
而不会暂停,并且还会调用 oSearch
函数。
这个onSearch
方法是主应用传下来的prop方法:
<Search onSearch={ text => dispatch(search(text)) } />
redux dispatch 触发了一个 redux 搜索操作,如下所示:
export function searchPerformed(search) {
return {
type: SEARCH_PERFORMED
}
}
我不知道为什么会发生这种情况 - 我猜这与 redux 有关,因为问题发生在 handleSearch 调用 onSearch 时,它又会在父组件中触发 redux 调度。
问题是第一次执行的时候就走到了else,调用了dispatch函数。 reducer 可能会立即更新一些状态,并导致重新渲染;重新渲染导致再次创建输入,新的 'throttle closure' 再次具有 null 'last' 和 'deferTimer' -> 每次都转到 else,因此立即更新。
感谢 luanped 帮我解决了这个问题。有了这个理解,我就能找到一个简单的解决方案。搜索组件不需要更新,因为输入是不受控制的组件。为了阻止我遇到的周期性问题,我使用了 shouldComponentUpdate 来防止它重新渲染:
constructor() {
super();
this.handleSearch = _.throttle(this.handleSearch,1000);
}
shouldComponentUpdate() {
return false;
}
我还将油门移到了构造函数中,因此油门只能有一个实例。
我认为这是一个很好的解决方案,但是我才刚刚开始学习 React,所以如果有人可以指出这种方法的问题,我们将不胜感激。
正如 Mike 指出的那样,如果组件不需要更新,那么不更新组件就可以获得正确的行为。
在我的例子中,我有一个组件需要每隔几秒轮询一次服务器更新,直到某些状态派生的 prop 更改值(例如 'pending' 与 'complete')。
每次有新数据进来,组件都会重新渲染,并再次调用 action creator,限制 action creator 没有用。
我可以简单地通过将相关的动作创建者交给组件安装上的 setInterval 来解决。是的,这是在渲染上发生的副作用,但很容易推理,实际的状态变化仍然通过调度程序。
如果您想保持纯粹,或者您的用例更复杂,请查看 https://github.com/pirosikick/redux-throttle-actions。