反应:在工作时更改按钮文本

时间:2019-01-05 21:46:22

标签: javascript reactjs

在我的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;

这不起作用,因为setStatethis.props.onClick()(由父组件传递)都是异步执行的,因此调用立即返回,并且对state的更改可能会丢失在React优化中(可能只在几毫秒内可见...)。

我在component state上进行了阅读,并决定lift up state属于它的位置,父组件,按钮保存其更改的表单(在我的应用中,它是父辈而不是父辈) 。所以我从isSaving中删除了SaveButton,替换为

const { isWorking } = this.state;

作者

const { isWorking } = this.props.isWorking;

,并尝试在表单父组件中设置状态。但是,这在结构上可能更干净,但实际上只会将我的问题转移到其他地方:

实际的保存功能是在应用程序中完全不同的位置完成的。为了触发它,我将onClick事件处理程序作为道具传递给组件树。单击该按钮后,呼叫链将备份该树。但是,如何向相反方向(即沿着树向下)通知操作已完成?

我的问题:如何通知保存状态为已完成的表单组件?

该表单的父组件(知道保存已完成)可以使用ref,但不仅有这些表单之一,而且还有整个数组。

我是否必须建立一个引用列表,每个表单一个? 还是在forwarding refs合适的情况下?

非常感谢您的考虑! :-)

4 个答案:

答案 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秒的超时来模拟保存。