【问题标题】:Unable to update the state when button inside table cell clicked , React Ant Design单击表格单元格内的按钮时无法更新状态,React Ant Design
【发布时间】:2019-03-16 23:51:55
【问题描述】:

我想在点击表格单元格时更新状态。但是事件handleClick 没有触发。如果我在类外声明handleClick,则事件调用发生但状态未定义消息即将到来。

如果我们单击类外的其他单元格,如何更新状态。 下面是我的代码

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Table, Icon, Switch, Radio, Form, Divider, Button, Modal } from 'antd';

import * as productActions from './../../redux/actions/productActions';

const handleClick = (product) => {
  debugger;
  
  //UNABLE TO UPDATE STATE HERE
  // this.setState({
  //   visibleDeletePopup: true
  // });
  alert(JSON.stringify(product));
}

const columns = [{
  title: 'Name',
  dataIndex: 'name',
  key: 'name',
  width: 150,
  render: text => <a href="javascript:;">{text}</a>,
}, {
  title: 'Full',
  dataIndex: "stockIn['full']",
  key: `stockIn['full'`,
  width: 70,
},
{
  title: 'Half',
  dataIndex: "stockIn['half']",
  key: `stockIn['half'`,
  width: 70,
},
{
  title: 'Quarter',
  dataIndex: "stockIn['quarter']",
  key: `stockIn['quarter'`,
  width: 70,
},
{
  title: '90',
  dataIndex: "stockIn['ninty']",
  key: `stockIn['ninty']`,
  width: 70,
}, {
  title: 'Category',
  dataIndex: 'category',
  key: 'category',
}, {
  title: 'Action',
  key: 'action',
  width: 360,
  render: (text, record) => (<span>
    <button href="javascript:;" onClick={() => handleClick(record)}>Edit-{record.name}</button>
    {/* <Divider type="vertical" />
    <a href="javascript:;">Delete</a>
    <Divider type="vertical" />
    <a href="javascript:;" className="ant-dropdown-link">
      More actions <Icon type="down" />
    </a> */}
  </span>
  ),
}];


const showHeader = true;
let footer = () => 0;
const scroll = { y: 300 };
const pagination = { position: 'bottom' };


class ProductsPage extends React.Component {
  constructor(props) {
    super(props);
    this.onProductSave = this.onProductSave.bind(this);
    this.onChange = this.onChange.bind(this);

    this.state = {
      bordered: true,
      loading: false,
      pagination,
      size: 'small',
      expandedRowRender,
      title: title,
      showHeader,
      footer,
      rowSelection: {},
      hasData: true,
    };

    //Popup and submit button
    this.state.buttonSubmitLoader = false;
    this.state.visibleDeletePopup = false;
  }


  handleClick = (product) => {
    //UPDATE STATE // how?
  }

  showModal = () => {
    this.setState({
      visibleDeletePopup: true,
    });
  }

  handleOk = () => {
    this.setState({ buttonSubmitLoader: true });
    setTimeout(() => {
      this.setState({ buttonSubmitLoader: false, visibleDeletePopup: false });
    }, 3000);
  }

  handleCancel = () => {
    this.setState({ visibleDeletePopup: false });
  }


  render() {
    const state = this.state;
    return (
      <div>
        <div>
          <Table {...this.state}
            columns={columns}
            dataSource={state.hasData ? this.props.products : null}
            footer={() => this.getFooterDetails(this.props.products)}
            pagination={{ pageSize: 5 }}
          />
        </div>
       
      </div>
    );
  }

  componentDidMount() {
    const props = this.props;
    props.actions.loadProducts();
  }

  courseRow(item, index) {
    return <li key={index}>{item.name}</li>;
  }

  onProductSave(product) {

    this.props.actions.createProduct(product);
    this.setState({
      product: ""
    });
  }

  onChange(e) {
    this.setState({
      product: e.target.value
    });
  }

  getFooterDetails(products) {
    return <label class="text-success">Total Records Count is {products.length}</label>;
  }

}


function mapStateToProps(state, ownProps) {
  //In state.products, product is coming from root reducer, if you change 
  //the name products to abhi_products , then here you need to call products:state.abhi_products 
  return {
    products: state.products
  }
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(productActions, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ProductsPage);

   

【问题讨论】:

    标签: reactjs antd


    【解决方案1】:

    setState 仅在组件内有效。如果你想在声明的函数中执行 setState 并且想要执行 setState 则必须将此上下文传递给函数。

    下面将在组件内工作

      handleClick = product => {
          this.setState({
             visibleDeletePopup: true
         });
      }
    

    更新:

    整个组件代码可以修正如下

    import React from 'react';
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    import { Table, Icon, Switch, Radio, Form, Divider, Button, Modal } from 'antd';
    
    import * as productActions from './../../redux/actions/productActions';
    
    
    let footer = () => 0;
    
    
    class ProductsPage extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          bordered: true,
          loading: false,
          pagination: { position: 'bottom' },
          size: 'small',
          expandedRowRender,
          title: title,
          showHeader: true,
          footer,
          rowSelection: {},
          hasData: true,
        };
    
         handleClick = product => {
            this.setState({
               visibleDeletePopup: true
           });
        }
        //Popup and submit button
        this.state.buttonSubmitLoader = false; // never mutate state like this instead use setState method
        this.state.visibleDeletePopup = false;
      }
    
      showModal = () => {
        this.setState({
          visibleDeletePopup: true,
        });
      }
    
      handleOk = () => {
        this.setState({ buttonSubmitLoader: true });
        setTimeout(() => {
          this.setState({ buttonSubmitLoader: false, visibleDeletePopup: false });
        }, 3000);
      }
    
      handleCancel = () => {
        this.setState({ visibleDeletePopup: false });
      }
    
      componentDidMount() {
        const props = this.props;
        props.actions.loadProducts();
      }
    
      courseRow = (item, index) => {
        return <li key={index}>{item.name}</li>;
      }
    
      onProductSave = product => {
        this.props.actions.createProduct(product);
        this.setState({
          product: ""
        });
      }
    
      onChange = e => {
        this.setState({
          product: e.target.value
        });
      }
    
      getFooterDetails = products => {
        return <label class="text-success">Total Records Count is {products.length}</label>;
      }
    
    
      render() {
        const { hasData } = this.state;
        const { products } = this.props;
        const columns = [{
          title: 'Name',
          dataIndex: 'name',
          key: 'name',
          width: 150,
          render: text => <a href="javascript:;">{text}</a>,
        }, {
          title: 'Full',
          dataIndex: "stockIn['full']",
          key: `stockIn['full'`,
          width: 70,
        },
        {
          title: 'Half',
          dataIndex: "stockIn['half']",
          key: `stockIn['half'`,
          width: 70,
        },
        {
          title: 'Quarter',
          dataIndex: "stockIn['quarter']",
          key: `stockIn['quarter'`,
          width: 70,
        },
        {
          title: '90',
          dataIndex: "stockIn['ninty']",
          key: `stockIn['ninty']`,
          width: 70,
        }, {
          title: 'Category',
          dataIndex: 'category',
          key: 'category',
        }, {
          title: 'Action',
          key: 'action',
          width: 360,
          render: (text, record) => (<span>
            <button href="javascript:;" onClick={() => this.handleClick(record)}>Edit-{record.name}</button>
          </span>
          ),
        }];
        return (
          <div>
            <div>
              <Table {...this.state}
                columns={columns}
                dataSource={hasData ? products : null}
                footer={() => this.getFooterDetails(products)}
                pagination={{ pageSize: 5 }}
              />
            </div>
          </div>
        );
      }
    }
    
    
    const mapStateToProps = (state, ownProps) => {
      //In state.products, product is coming from root reducer, if you change 
      //the name products to abhi_products , then here you need to call products:state.abhi_products 
      return {
        products: state.products
      }
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        actions: bindActionCreators(productActions, dispatch)
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(ProductsPage);
    

    【讨论】:

    • 你能给我举个例子吗?如果我将 handleclick() 放在类中,则列渲染在类之外。它无法找到该方法。 (能不能同时显示html和js)
    • 所以,你要求我在类本身中声明列,以便可以访问 handleClick 方法。
    • @Abhi 是的,这就是您需要在 react 中实现的方式,确保您将所有内容保存在组件中。我在回答中做了一些小的更正。现在可以了
    • 检查并告诉我
    • 我正在检查。antd 组件很大 (html, events) 。制作我的自定义组件并将antd组件放在里面还是保持原样(文件可能会越来越大)的最佳实践是什么
    【解决方案2】:

    您可以像这样在类构造函数中声明列变量:

    this.columns = []
    

    事件将是(当然你需要绑定handleClick):

    onClick={() => this.handleClick(record)}
    

    或者你可以像你一样在外面声明列并使用类上下文调用事件处理程序:

    onClick={() => ProductsPage.prototype.handleClick(record)}
    

    【讨论】:

    • 未捕获类型错误:ProductsPage.prototype.handleClick 不是函数
    【解决方案3】:
        enter code here
    
    const getColumns = (handleClick) => { // Note here
      return [
       {
          title: 'Name',
      dataIndex: 'name',
      key: 'name',
      width: 150,
      render: text => <a href="javascript:;">{text}</a>,
    }, {
      title: 'Full',
      dataIndex: "stockIn['full']",
      key: `stockIn['full'`,
      width: 70,
    },
    {
      title: 'Half',
      dataIndex: "stockIn['half']",
      key: `stockIn['half'`,
      width: 70,
    },
    {
      title: 'Quarter',
      dataIndex: "stockIn['quarter']",
      key: `stockIn['quarter'`,
      width: 70,
    },
    {
      title: '90',
      dataIndex: "stockIn['ninty']",
      key: `stockIn['ninty']`,
      width: 70,
    }, {
      title: 'Category',
      dataIndex: 'category',
      key: 'category',
    }, {
      title: 'Action',
      key: 'action',
      width: 360,
      render: (text, record) => (<span>
        <button href="javascript:;" onClick={() => handleClick(record)}>Edit-{record.name}</button>
        {/* <Divider type="vertical" />
        <a href="javascript:;">Delete</a>
        <Divider type="vertical" />
        <a href="javascript:;" className="ant-dropdown-link">
          More actions <Icon type="down" />
        </a> */}
      </span>
      ),
    }]
    }
    

    然后在你的组件里面

     <Table {...this.state}
        columns={getColumns(this.handleClick)} // and Here
        dataSource={state.hasData ? this.props.products : null}
        footer={() => this.getFooterDetails(this.props.products)}
        pagination={{ pageSize: 5 }}
       />
    

    【讨论】:

      【解决方案4】:

      你可以使用列标签

      <Table pagination={false} dataSource={this.state.responsible}>
          <Column title="name" dataIndex="name" key="name" />
          <Column title="Category" dataIndex="email" key="Category" />
      
        <Column title="Actions" dataIndex="Actions" key="Actions"
       render={(text, element, index)=>{
         return (<div>
          <i className="fas fa-edit text-success m-1" style={{ cursor: "pointer" }}
            onClick={() => this.handleClick()}
          ></i>
          <i className="fas fa-trash text-danger m-1" style={{ cursor: "pointer" }}
            onClick={() => {this.Delete(element._id)
       
            }}
          ></i>
      
        </div>)
       }}
        />
            
            </Table>   
      

      【讨论】:

        猜你喜欢
        • 2020-12-25
        • 2012-11-18
        • 1970-01-01
        • 1970-01-01
        • 2022-07-08
        • 1970-01-01
        • 2011-02-06
        • 1970-01-01
        • 2015-07-10
        相关资源
        最近更新 更多