在我的React应用程序中,我希望有一个通常显示Save
的按钮,单击后将其文本更改为Saving...
,并在保存后将其更改回Save
完成。
这是我的第一次尝试:
import React from 'react';
import { Button } from 'react-bootstrap';
class SaveButton extends React.Component {
constructor(props) {
super(props);
this.state = { isSaving : false };
this.onClick = this.onClick.bind(this);
}
onClick() {
// DOES NOT WORK
this.setState({ isSaving : true });
this.props.onClick();
this.setState({ isSaving : false });
}
render() {
const { isWorking } = this.state;
return (
<Button bsStyle="primary"
onClick={isWorking ? null : this.onClick}>
{isWorking ? 'Saving...' : 'Save'}
</Button>
);
}
}
export default SaveButton;
这不起作用,因为setState
和this.props.onClick()
(由父组件传递)都是异步执行的,因此调用立即返回,并且对state
的更改可能会丢失在React优化中(可能只在几毫秒内可见...)。
我在component state上进行了阅读,并决定lift up state属于它的位置,父组件,按钮保存其更改的表单(在我的应用中,它是父辈而不是父辈) 。所以我从isSaving
中删除了SaveButton
,替换为
const { isWorking } = this.state;
作者
const { isWorking } = this.props.isWorking;
,并尝试在表单父组件中设置状态。但是,这在结构上可能更干净,但实际上只会将我的问题转移到其他地方:
实际的保存功能是在应用程序中完全不同的位置完成的。为了触发它,我将onClick
事件处理程序作为道具传递给组件树。单击该按钮后,呼叫链将备份该树。但是,如何向相反方向(即沿着树向下)通知操作已完成?
我的问题:如何通知保存状态为已完成的表单组件?
该表单的父组件(知道保存已完成)可以使用ref,但不仅有这些表单之一,而且还有整个数组。
我是否必须建立一个引用列表,每个表单一个? 还是在forwarding refs合适的情况下?
非常感谢您的考虑! :-)
答案 0 :(得分:1)
这不起作用,因为setState和this.props.onClick() (由父组件传递)是异步执行的,因此 呼叫立即返回,并且状态更改可能丢失 在React优化中
setState
可以take a callback通知您,状态一旦更新,您就可以:
onClick() {
this.setState({ isSaving : true }, () => {
this.props.onClick();
this.setState({ isSaving : false });
});
}
如果this.props.onClick()
也是异步的,则将其变成一个承诺:
onClick() {
this.setState({ isSaving : true }, () => {
this.props.onClick().then(() => {
this.setState({ isSaving : false });
});
});
}
答案 1 :(得分:0)
你看到了吗? https://developer.mozilla.org/tr/docs/Web/JavaScript/Reference/Global_Objects/Promise
父组件的onClick函数
onClick(){
return new Promise(function(resolve,reject){
//example code
for(let i=0;i<100000;i++){
//or api call
}
resolve();
})
}
SaveButton组件的onClick功能
onClick(){
this.setState({ isSaving : true });
this.props.onClick().then(function(){
this.setState({ isSaving : false });
});
}
答案 2 :(得分:0)
onClick() {
this.setState({ isSaving : true });
if(this.props.onClick){
this.props.onClick(); // This probably where you would have an `ajax` call
}
setTimeout(() => {
// Completed of async action, set loading state back
this.setState({ isSaving: false });
}, 2000);
}
可能是最好的方法,React引导程序在此页面上有一个示例,您可以参考https://react-bootstrap.github.io/components/buttons/
如果无法解决问题,很高兴进一步讨论。如果您能够提供一个Codepen,那也会很有用。
答案 3 :(得分:0)
这是您有效的代码:
import React from 'react';
import { Button } from 'react-bootstrap';
class SaveButton extends React.Component {
constructor() {
super();
this.state = {
isSaving: false
};
this.handleOnClick = this.handleOnClick.bind(this);
}
handleOnClick() {
this.setState(prevState => {
return {
isSaving: !prevState.isSaving
}
});
console.log("Clicked!",this.state.isSaving);
this.handleSave();
}
handleSave() {
// Do what ever and if true => set isSaving = false
setTimeout(()=>{
this.setState(prevState=>{
return {
isSaving: !prevState.isSaving
}
})
},5000)
}
render() {
const isWorking = this.state.isSaving;
return (
<Button
bsStyle="primary"
onClick={this.handleOnClick}
>
{isWorking ? 'Saving...' : 'Save'}
</Button>
);
}
}
export default SaveButton;
您需要更改const isWorking
而不是const {isWorking}
,并且需要以某种方式处理保存功能。
在此示例中,我使用5秒的超时来模拟保存。