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> )
}
}
}]
@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!