Reactjs 如何处理类组件中的竞争条件?
假设有一个组件,其中要求服务器执行一些搜索并提供响应。如何确保即使服务器端出于任何原因以不同的顺序应答,也能提供最新请求的响应?我不是要取消以前的请求,因为经过合理的努力,这并不总是可能的Reactjs 如何处理类组件中的竞争条件?,reactjs,Reactjs,假设有一个组件,其中要求服务器执行一些搜索并提供响应。如何确保即使服务器端出于任何原因以不同的顺序应答,也能提供最新请求的响应?我不是要取消以前的请求,因为经过合理的努力,这并不总是可能的 onClick = () => { apiCall(this.state.searchQuery).then(items => this.setState({ items })); }; 有没有优雅的处理方法?到目前为止,我知道的方法很少: 在请求到来之前禁用按钮(在大量情况下提供不好
onClick = () => {
apiCall(this.state.searchQuery).then(items => this.setState({ items }));
};
有没有优雅的处理方法?到目前为止,我知道的方法很少:
then()
如果请求的参数与this.props
/this.state
数据匹配(当我们故意用相同的查询强制进行新搜索时,不处理这种情况-比如按Enter/单击“搜索”按钮) useEffect(() => {
const isCanceled = false;
apiCall(searchQuery).then(items => !isCanceled && setItems(items));
return () => {isCanceled = true;};
}, [searchQuery])
您的onClick处理程序建议类组件,因为您使用了
this
和this.setState
:
onClick = () => {
apiCall(this.state.searchQuery).then(items =>
this.setState({ items })
);
};
我调整了onlyLastRequestedPromise
以获取一个函数,该函数将返回某些内容(您可以返回Promise.reject('cancelled')或任何内容)
关于如何使用它的课堂示例:
class Component extends React.Component {
CANCELLED = {};
last = onlyLastRequestedPromise(
'search',
() => this.CANCELLED
);
onSearch = () => {
this.last(apiCall(this.state.searchQuery)).then(
items =>
items !== this.CANCELLED && this.setState({ items })
);
};
changeAndSearch = e => {
this.setState(
{}, //state with new value
() => this.onSearch() //onSearch after state update
);
};
render() {
return (
<div>
<SearchButton onClick={this.onSearch} />
<Other onChange={this.changeAndSearch} />
</div>
);
}
}
最终从hooks的世界中找到了如何利用闭包来模仿“忽略那个”方法:
class MyComponent extends React.Component {
const ignorePrevRequest = () => {}; // empty function by default
loadSomeData() {
this.ignorePrevRequest();
let cancelled = false;
this.ignorePrevRequest = () => { cancelled = true; }; // closure comes to play
doSomeCall().then(data => !cancelled && this.setState({ data }))
}
}
所以你;I’我希望承诺仅在用户最近一次单击时解决?类似于?@HMR,是的,类似于我的#3,但搬到了外部助手/图书馆。你能知道其他选择吗?当然,它可以工作,但看起来相当复杂。它会清理您的第三个选项,看起来非常像useEffect,
last
可以是您的类的属性:last=onlyLastRequestedPromise(“search”)
并且在promise或任何您调用api的地方:this.last(apiCall(this.state.searchQuery))。然后(items=>items!==CANCELLED&&this.setState({items}));
只有onlyLastRequestedPromise
才能用一个名为CANCELLED的常量进行解析。实际上,我的意思是searchQueryIndex
是类属性,但我明白了,它不清楚。你能用你的建议运行官方答案吗?“您的onClick处理程序建议类组件”实际上是在标题中:)@skyboyer是的,你是对的,我被useEffect钩子弄糊涂了,它看起来更干净,但在渲染时会做一些事情,而不会在单击时做一些事情。目标是找出如何获得类组件的干净方法,就像useEffect
所允许的那样。你认为我最好重写这个问题,让它更清楚吗?@skyboyer不,我想大多数人都能理解。如果你有功能组件,你可以把last作为一个定制的钩子来编写:const last=useLast(apiCall);
而onSearch就是:last(searchQuery)。然后(result=>对result做点什么)
构建最后一个和挂载的签入useLast钩子。
const onlyLastRequestedPromise = (promiseIds => {
const whenResolve = (
promise,
id,
promiseID,
resolveValue,
whenCancelled = () => Promise.reject('cancelled')
) => {
if (promise !== undefined) {
//called by user adding a promise
promiseIds[id] = {};
} else {
//called because promise is resolved
return promiseID === promiseIds[id]
? Promise.resolve(resolveValue)
: whenCancelled(resolveValue);
}
return (function(currentPromiseID) {
return promise.then(function(result) {
return whenResolve(
undefined,
id,
currentPromiseID,
result
);
});
})(promiseIds[id]);
};
return (id = 'general', whenCancelled) => promise =>
whenResolve(
promise,
id,
undefined,
undefined,
whenCancelled
);
})({});
class Component extends React.Component {
CANCELLED = {};
last = onlyLastRequestedPromise(
'search',
() => this.CANCELLED
);
onSearch = () => {
this.last(apiCall(this.state.searchQuery)).then(
items =>
items !== this.CANCELLED && this.setState({ items })
);
};
changeAndSearch = e => {
this.setState(
{}, //state with new value
() => this.onSearch() //onSearch after state update
);
};
render() {
return (
<div>
<SearchButton onClick={this.onSearch} />
<Other onChange={this.changeAndSearch} />
</div>
);
}
}
//
function ComponentContainer(props) {
const CANCELLED = useRef({});
const last = useRef(
onlyLastRequestedPromise('search', () => CANCELLED)
);
const [searchQuery,setSearchQuery] = useState({});
const mounted = useIsMounted();
const onSearch = useCallback(
last(apiCall(searchQuery)).then(
items =>
items !== CANCELLED &&
mounted.current &&
//do something with items
)
);
}
class MyComponent extends React.Component {
const ignorePrevRequest = () => {}; // empty function by default
loadSomeData() {
this.ignorePrevRequest();
let cancelled = false;
this.ignorePrevRequest = () => { cancelled = true; }; // closure comes to play
doSomeCall().then(data => !cancelled && this.setState({ data }))
}
}