Javascript ReactJS:当组件状态为';它是基于AJAX的吗?
摘要:ReactJS新手,我正在尝试找出更新组件的最佳方法,因为组件的状态取决于远程API(即通过AJAX API保持组件状态与远程数据库同步) 示例用例:设想一个产品库存,单击按钮将产品添加到购物车中,并将库存减少1。每次用户单击该组件时,它都会启动一个AJAX请求,然后在请求完成后,该组件通过调用Javascript ReactJS:当组件状态为';它是基于AJAX的吗?,javascript,reactjs,Javascript,Reactjs,摘要:ReactJS新手,我正在尝试找出更新组件的最佳方法,因为组件的状态取决于远程API(即通过AJAX API保持组件状态与远程数据库同步) 示例用例:设想一个产品库存,单击按钮将产品添加到购物车中,并将库存减少1。每次用户单击该组件时,它都会启动一个AJAX请求,然后在请求完成后,该组件通过调用setState()重新呈现新产品清单 问题:我遇到了一个问题,因为setState()和AJAX请求都是异步的,所以组件与服务器不同步。例如,如果您非常快速地单击,您可以为单个产品ID启动多个AJ
setState()
重新呈现新产品清单
问题:我遇到了一个问题,因为setState()
和AJAX请求都是异步的,所以组件与服务器不同步。例如,如果您非常快速地单击,您可以为单个产品ID启动多个AJAX请求,因为组件的状态尚未更新以反映产品ID不再在库存中。下面我举一个简单的例子来说明这个概念:
解决方案不充分:如果客户端请求不再在库存中的产品,则可以通过向服务器端发送错误来处理此问题,然而,我确实在寻找在客户端处理ReactJS中常见场景的最佳方法,并确保我了解处理组件状态的最佳方法
Component extends React.Component {
constructor(props) {
super(props);
this.state = {
clicksLeft: 0,
};
}
componentDidMount() {
//getClicksLeft is async and takes a callback, think axios/superagent
getClicksLeft((response) => {
this.setState(response);
});
}
btnClicked = () => {
//This may appear redundant/useless but
//imagine sending an element in a list and then requesting the updated
//list back
const data = {clicks: this.state.clicksLeft--};
decrementClicksLeft(data, () => {
getClicksLeft((response) => {
this.setState(response);
});
}
}
render() {
<button onClick={this.btnClicked}>Click me {this.state.clicksLeft} times</button>
}
}
组件扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
单击左键:0,
};
}
componentDidMount(){
//getClicksLeft是异步的,并接受回调,比如axios/superagent
getClicksLeft((响应)=>{
此.setState(响应);
});
}
btnClicked=()=>{
//这可能显得多余/无用,但是
//想象一下,发送列表中的一个元素,然后请求更新的
//列回
const data={clicks:this.state.clicksLeft-->;
递减ICKSLEFT(数据,()=>{
getClicksLeft((响应)=>{
此.setState(响应);
});
}
}
render(){
单击我{this.state.clicksLeft}次
}
}
当单击按钮时,是否有任何理由必须调用getClicksLeft
?在安装组件时,您已经调用了它,然后只要单击按钮,您就可以将该数字减少1
btnClicked = () => {
if (this.state.clicksLeft > 0) {
decrementClicksLeft();
this.setState({clicksLeft: this.state.clicksLeft - 1});
}
}
如果一次只有一个用户想买东西,这就行了。否则你也可以在购买之前检查剩余的数量
btnClicked = () => {
getClicksLeft((response) => {
if (response > 0) {
decrementClicksLeft();
this.setState({clicksLeft: this.state.clicksLeft - 1});
}
});
}
这样,如果没有点击,什么也不会发生。最基本的解决方案是在等待响应返回时禁用按钮: (我还简化了代码。)
组件扩展了React.Component{
建造师(道具){
超级(道具);
//初始状态
此.state={
clicksLeft:0,//没有可用的单击
makeRequest:false,//我们没有要求向服务器发出请求
pendingTransaction:false,//当前没有向服务器发出请求
};
}
componentDidMount(){
//初始加载已完成,请获取点击次数
这是。_getClicksRemaining();
}
//当道具/状态改变时调用
//未为初始渲染调用
组件将更新(下一步,下一步状态){
//如果没有向服务器发出现有请求,并且如果
//州政府要求我们提出请求(如handleButtonClick中所述)
//然后去提出请求
if(!this.state.pendingTransaction&&nextState.makeRequest){
常数数据={
单击:this.state.clicksLeft--,
};
//递减ICKSLEFT是异步的
递减icksleft(数据,()=>this._getClicksRemaining());
//首先触发上面的异步递减ICKSLEFT请求,然后
//告诉组件有一个挂起的请求,并且
//不允许尝试提出新请求
//注意,这是代码中易受攻击的部分
//最初的问题,理论上用户可以单击按钮
//在此设置状态完成之前再次执行。但是,如果您的用户能够
//要做到这一点,他们可能正在使用脚本,而你不应该这样做
//担心它们。设置状态/渲染速度非常快,所以这应该是
//足够的保护,防止人为点击
这是我的国家({
makeRequest:false,
pendingTransaction:对,
});
}
}
_getClicksRemaining(){
//getClicksLeft是异步的
getClicksLeft((响应)=>{
//这里我们在getClicksLeft的回调中,因此
//知道它已经完成。所以,重置我们的标志以显示它
//没有尚未处理的请求
const newState=Object.assign(
{
pendingTransaction:错误,
},
答复,,
);
this.setState(newState);
});
}
//按钮被点击了
_把手按钮点击=()=>{
如果(!this.state.pendingTransaction){
//如果当前没有向服务器发出请求,则可以安全地
//创建一个新的。在此处设置状态将导致'componentWillUpdate'`
//被叫
这是我的国家({
makeRequest:true,
});
}
}
render(){
//如果出现以下情况,请禁用该按钮:
//*没有点击
//*服务器有一个挂起的请求
const buttonDisabled=((this.state.clicksLeft==0)| | this.state.pendingTransaction);
返回(
单击我{this.state.clicksLeft}次
);
}
}
在花了一段时间使用react-redux、redux-thunk和redux-pack之后,我决定使用更简单的方法:。我并不真正需要redux的复杂性,因为我只是在列表上执行post和get操作。我还需要一些简单的副作用,比如当我发布一篇文章时,我需要更新多个列表(这是通过实现的,然后
Component extends React.Component {
constructor(props) {
super(props);
// Initial state
this.state = {
clicksLeft: 0, // No clicks are availabe
makeRequest: false, // We are not asking to make a request to the server
pendingTransaction: false, // There is no current request out to the server
};
}
componentDidMount() {
// Initial load completed, so go get the number of clicks
this._getClicksRemaining();
}
// Called whenever props/state change
// NOT called for the initial render
componentWillUpdate(nextProps, nextState) {
// If there is no existing request out to the server, AND if the next
// state is asking us to make a request (as set in _handleButtonClick)
// then go make the request
if (!this.state.pendingTransaction && nextState.makeRequest) {
const data = {
clicks: this.state.clicksLeft--,
};
// decrementClicksLeft is async
decrementClicksLeft(data, () => this._getClicksRemaining());
// First fire off the async decrementClicksLeft request above, then
// tell the component that there is a pending request out, and that it
// is not allowed to try and make new requests
// NOTE this is the one part of the code that is vulnerable to your
// initial problem, where in theory a user could click the button
// again before this setState completes. However, if your user is able
// to do that, then they are probably using a script and you shouldn't
// worry about them. setState/render is very fast, so this should be
// more than enough protection against human clicking
this.setState({
makeRequest: false,
pendingTransaction: true,
});
}
}
_getClicksRemaining() {
// getClicksLeft is async
getClicksLeft((response) => {
// Here we are inside of the callback from getClicksLeft, so we
// know that it has completed. So, reset our flags to show that
// there is no request still pending
const newState = Object.assign(
{
pendingTransaction: false,
},
response,
);
this.setState(newState);
});
}
// The button was clicked
_handleButtonClick = () => {
if (!this.state.pendingTransaction) {
// If there isn't a request out to the server currently, it's safe to
// make a new one. Setting state here will cause `componentWillUpdate`
// to get called
this.setState({
makeRequest: true,
});
}
}
render() {
// Disable the button if:
// * there are no clicks left
// * there is a pending request out to the server
const buttonDisabled = ((this.state.clicksLeft === 0) || this.state.pendingTransaction);
return (
<button
disabled={buttonDisabled}
onClick={this._handleButtonClick}
>
Click me {this.state.clicksLeft} times
</button>
);
}
}