Skip to content

Change color of dropdown item in React with color picker

I have a react color picker that I am using to create a custom 16 color gradient. I use a dropdown to select the color I want to edit and then use the color picker to pick the color. This edits an array that is called to style each box of the dropdown. My end goal is to change the background of each entry in the dropdown to the corresponding color in the gradient.

var gradientColors = ['#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000'];

function ChangeCustomGradientColor(eventKey, color) {
  gradientColors[eventKey] = color;
}

function App() {
const [color, setColor] = useState('#fff')
const [showColorPicker, setShowColorPicker] = useState(false)
const [eKey, setEventKey] = useState('');
const eventKeySelect=(e)=>{
  setEventKey(e)
  setShowColorPicker(showColorPicker => !showColorPicker)
}

return (
<div className="App" id="top">
  <header className="App-header" id="mid">  
    <Dropdown onSelect={eventKeySelect}>
      <Dropdown.Toggle variant="success" id="custom-color-change">
        Change Custom Gradient Color
      </Dropdown.Toggle>

      <Dropdown.Menu >
        <Dropdown.Item id="cust0" eventKey="0" style={{backgroundColor:gradientColors[0]}}>1</Dropdown.Item>
        <Dropdown.Item id="cust1" eventKey="1" style={{backgroundColor:gradientColors[1]}}>2</Dropdown.Item>
        <Dropdown.Item id="cust2" eventKey="2" style={{backgroundColor:gradientColors[2]}}>3</Dropdown.Item>
        <Dropdown.Item id="cust3" eventKey="3" style={{backgroundColor:gradientColors[3]}}>4</Dropdown.Item>
        <Dropdown.Item id="cust4" eventKey="4" style={{backgroundColor:gradientColors[4]}}>5</Dropdown.Item>
        <Dropdown.Item id="cust5" eventKey="5" style={{backgroundColor:gradientColors[5]}}>6</Dropdown.Item>
        <Dropdown.Item id="cust6" eventKey="6" style={{backgroundColor:gradientColors[6]}}>7</Dropdown.Item>
        <Dropdown.Item id="cust7" eventKey="7" style={{backgroundColor:gradientColors[7]}}>8</Dropdown.Item>
        <Dropdown.Item id="cust8" eventKey="8" style={{backgroundColor:gradientColors[8]}}>9</Dropdown.Item>
        <Dropdown.Item id="cust9" eventKey="9" style={{backgroundColor:gradientColors[9]}}>10</Dropdown.Item>
        <Dropdown.Item id="cust10" eventKey="10" style={{backgroundColor:gradientColors[10]}}>11</Dropdown.Item>
        <Dropdown.Item id="cust11" eventKey="11" style={{backgroundColor:gradientColors[11]}}>12</Dropdown.Item>
        <Dropdown.Item id="cust12" eventKey="12" style={{backgroundColor:gradientColors[12]}}>13</Dropdown.Item>
        <Dropdown.Item id="cust13" eventKey="13" style={{backgroundColor:gradientColors[13]}}>14</Dropdown.Item>
        <Dropdown.Item id="cust14" eventKey="14" style={{backgroundColor:gradientColors[14]}}>15</Dropdown.Item>
        <Dropdown.Item id="cust15" eventKey="15" style={{backgroundColor:gradientColors[15]}}>16</Dropdown.Item>
      </Dropdown.Menu>
    </Dropdown>
    {
    showColorPicker && (
    <ChromePicker 
      disableAlpha={true}
      color={color} 
      onChangeComplete={updatedColor => ChangeCustomGradientColor(eKey, updatedColor)}
    />
    )}
  </header>
</div>
)
}

I’ve also tried getElementByID in my ChangeCustomGradientColor function like so

function ChangeCustomGradientColor(eventKey, color) {
  let elementID = "cust" + eventKey;
  document.getElementByID(elementID).style.backgroundColor = color;
}

I’ve been copying/pasting and learning but now am at the point where I just need to sit down and take a full JS class before I move on. Any help or advice is appreciated.

Answer

The gradientColors are being mutated by reference instead of state, so your Dropdown.Item components go stale, they never know when to re-render. To fix that you just bring gradientColors into state with a useState. Here’s a codesandebox example.

instead of mutating

  initialGradientColors[eventKey] = color.hex;

you need to mutate via state so that it triggers the appropriate re-renders.

  const [gradientColors, setGradientColors] = useState(initialGradientColors);
  // in some callback 
  setGradientColors(prevGradientColors => {
   const updated = prevGradientColors.map((color, index) => {
    if(index === eventKey){
       return newColor;
    }
    return color;
   })
   return updated;
 }))