React-bootstrap-table2: Triggering formatting of cell when clicked

Created on 15 Oct 2019  路  4Comments  路  Source: react-bootstrap-table/react-bootstrap-table2

Question
I hope I did not miss docuemntation or another issue but I cannot find something on dynamically changing tables prompted by user interaction.

One of my columns is a color. I want to allow the user to change the color by clicking on it which should lead to a color picker opening that allows the user to select a new color for thi srow. I am runnning in the problem that the formatter is only run when the page is loaded but not again when a state changes. The state changes when the cell is clicked, in this case the displayColorPicker. How can I trigger that a change of a state triggers a new formatting of the cell or an onClick event triggers a new formatting of the cell?

this.columns = [
            {key: 'color',
                toggle: false,
                dataField:'color',
                text: 'Color',
                headerStyle: {
                    'color' : '#0E253A',
                    'fontFamily' : 'Teko',
                    'fontSize' : '20px',
                    'paddingBottom': '18px',
                    'fontWeight' : '400'
                },
                events: {
                    onClick: (e) => {
                        console.log('color clicked')
                        this.handleShowColorPicker()
                    },
                },
                formatExtraData: {
                    colorCheck: this.state.displayColorPicker
                  },
                formatter: (cell, row, rowIndex, formatExtraData) => {
                    if (formatExtraData['colorCheck']) { 
                        return(
                    <div className={"color-picker-container"}>
                    <div
                      className={"color-picker-field"}
                      name={"color-picker-field"}
                      style={{ width: "40px", height: "25px", backgroundColor: this.state.changeColor}}
                    />
                    <div className={"color-picker-palette"}>
                        <div
                          className={"color-picker-cover"}
                          onClick={() => this.handleCloseColorPicker()}
                        />
                        <ChromePicker
                          color={this.state.color}
                          onChange={this.changeColorPicker}
                        />
                    </div>
                    </div>)
                } else { 
                    this.setState({displayColorPicker : false})
                    return(
                    <div className={"color-picker-container"}>
                    <div
                      className={"color-picker-field"}
                      name={"color-picker-field"}
                      style={{ width: "30px", height: "25px", backgroundColor: this.state.changeColor}}
                    />
                    </div> )
                }
                }
            }]

All 4 comments

@chazarabriseis sorry for lately reply,
I can't read your code, it's too massive, if ok please put an example on codesandbox and keep it simple and minimal so that I can reproduce your issue.

However, I can't read your code but I guess you faced a issue is "you mutate the state after user clicking on the colorpicker"

Please keep your state immutable you might can avoid this bug.

Let me know if this issue still remain even you update state immutable, thanks

Hi @AllenFang,

I am attaching my problem in this codesandbox:
https://codesandbox.io/s/table-color-rerendering-problem-v1ysb

I am changing the state of the color in the color column with the color picker, however this is not reflected in the color column. I am new to js an react so it might be just a beginner's mistake that I am not aware of.

Please let me know if there is still something unclear.

@chazarabriseis

You mutate the state directly, try below

import React, { Component } from "react";
import ReactDOM from "react-dom";
import BootstrapTable from "react-bootstrap-table-next";
import "./styles.css";
import { ChromePicker } from "react-color";

class ProductList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      dataResultsTable: [
        { id: 1, name: "Item 1", price: 100, color: "#0000ff" },
        { id: 2, name: "Item 2", price: 102, color: "#f000ff" }
      ],
      selectedResultsTableIndex: 0,
      displayColorPicker: false,
      xPosColorPicker: "0px",
      yPosColorPicker: "0px",
      changeColor: "#999",
      color: {
        r: "0",
        g: "9",
        b: "153",
        a: "1"
      }
    };

    this.columns = [
      {
        dataField: "id",
        text: "Product ID"
      },
      {
        dataField: "name",
        text: "Product Name"
      },
      {
        dataField: "price",
        text: "Product Price"
      },
      {
        dataField: "color",
        text: "Color",
        formatter: cell => (
          <div
            style={{ width: "25px", height: "25px", backgroundColor: cell }}
          />
        ),
        events: {
          onClick: (e, column, columnIndex, row, rowIndex) => {
            // get the x,y coordinates of the mouse click
            document.addEventListener("click", getClickPosition, false);
            function getClickPosition(e) {
              return [e.clientX, e.clientY];
            }
            const [xPosClick, yPosClick] = getClickPosition(e);
            this.setState({ xPosColorPicker: `${xPosClick}px` });
            this.setState({ yPosColorPicker: `${yPosClick}px` });
            // get the color of the selected row
            var id = rowIndex;
            this.setState({ selectedResultsTableIndex: id });
            let clickedColor = this.state.dataResultsTable[id].color;
            this.setState({ color: clickedColor });
            // Make sure that if no new color is choosen the old one stays
            this.setState({ changeColor: clickedColor });
            // show the ColorPicker
            this.handleShowColorPicker();
          }
        }
      }
    ];
  }

  // color picker functions
  handleShowColorPicker = () => {
    // this will trigger to rerender with colorPicker right here
    this.setState({ displayColorPicker: true });
  };

  handleCloseColorPicker = () => {
    const newColor = this.state.changeColor;
    const selectedResultsTableIndex = this.state.selectedResultsTableIndex;

    let dataResultsTable = [...this.state.dataResultsTable];
    const oldColor = dataResultsTable[selectedResultsTableIndex].color;
    // dataResultsTable[selectedResultsTableIndex].color = newColor;

    // . This is correct way to update data immutability
    const newDataResultsTable = this.state.dataResultsTable.map((row, index) => {
      if (index === selectedResultsTableIndex) {
        return {
          ...row,
          color: newColor
        };
      }
      return { ...row };
    })

    this.setState({ dataResultsTable: newDataResultsTable });

    this.setState({ displayColorPicker: false });

    let dataResultsTable2 = [...this.state.dataResultsTable];
    const newStateColor = dataResultsTable2[selectedResultsTableIndex].color;
    console.log(`changing color from ${oldColor} to ${newStateColor}`);
  };

  changeColorPicker = color => {
    this.setState({ color: color.rgb, changeColor: color.hex });
  };

  render() {
    return (
      <div>
        <div style={{ padding: "20px" }}>
          <h1 className="h2">Products</h1>
          <BootstrapTable
            keyField="id"
            data={this.state.dataResultsTable}
            columns={this.columns}
          />
        </div>
        <div>
          {this.state.displayColorPicker && (
            <div
              style={{
                position: "fixed",
                top: this.state.yPosColorPicker,
                left: this.state.xPosColorPicker
              }}
            >
              <div className={"color-picker-container"}>
                <div className={"color-picker-palette"}>
                  <div
                    className={"color-picker-cover"}
                    onClick={() => this.handleCloseColorPicker()}
                  />
                  <ChromePicker
                    disableAlpha
                    color={this.state.color}
                    onChange={this.changeColorPicker}
                  />
                </div>
              </div>
            </div>
          )}
        </div>
        Check the console log that the color in the state changed but not in the
        table.
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<ProductList />, rootElement);

@AllenFang thanks so much. I thought using the spread operator and setState does the trick to change it in an immutable way, at least that's what I read when searching for it. But it seems it is different for nested states. Good to know! Sorry for bothering you and thanks a lot! Keep up the good work!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

josefheld picture josefheld  路  3Comments

primakashi picture primakashi  路  3Comments

harishkumarreddy12 picture harishkumarreddy12  路  3Comments

SandeepKapalawai picture SandeepKapalawai  路  3Comments

kamarajuPrathi picture kamarajuPrathi  路  4Comments