yeahhhmat
yeahhhmat

Reputation: 41

How to update styles dynamically in JSX

I am trying to update the individual style of each button when it is clicked, using the useRef() hook from React.

Right now, when I click any button the style change is always applied to the last button rendered.

I believe this is the bit needing attention but I'm stumped.

  const handleClick = () => {
    status.current.style.background = 'green';
  }  

Here's the full bit:

import React, { useRef } from 'react';
import ReactDOM from 'react-dom';
import './index.css';

let background = 'blue';
let controls = [];

const makeControls = () => {
  for (let i = 1; i <= 9; i++) {
    controls.push({active: false});
  }
  return controls;
};

const ControlPanel = () => {
  const status = useRef('blue');
  makeControls();

  const handleClick = () => {
    status.current.style.background = 'green';
  }  

  return (
    <>
      {controls.map((control, i) => (
        <div
          ref={status}
          style={{background: background}}
          className={'box'}
          key={i}
          onClick={() => handleClick()}></div>
      ))}
    </>
  );
};

ReactDOM.render(<ControlPanel />, document.getElementById('root'));

Upvotes: 1

Views: 105

Answers (3)

Mahdi mehrabi
Mahdi mehrabi

Reputation: 1744

you can use state to do that like this

import React, { useRef,useState } from 'react';
import ReactDOM from 'react-dom';
import './index.css';


let controls = [];

const makeControls = () => {
  for (let i = 1; i <= 9; i++) {
    controls.push({active: false});
  }
  return controls;
};

const ControlPanel = () => {
  const [controlState,setControlState]=useState({background:"blue"})
  const status = useRef('blue');
  makeControls();

  const handleClick = () => {
    setControlState({background:"green"});
  }  

  return (
    <>
      {controls.map((control, i) => (
        <div
          ref={status}
          style={{background: controlState.background}}
          className={'box'}
          key={i}
          onClick={() => handleClick()}></div>
      ))}
    </>
  );
};

ReactDOM.render(<ControlPanel />, document.getElementById('root'));

Upvotes: 0

bitDaft
bitDaft

Reputation: 177

When rendering the list of <div>s your status ref is getting reassigned each time, finally stopping on the last element.

which is why the last element gets updated.

Instead why not store the background color info on the control object itself

for (let i = 1; i <= 9; i++) {
    controls.push({active: false,background: 'blue'});
 }
{controls.map((control, i) => (
  <div
    style={{background: control.background}}
    className={'box'}
    key={i}
    onClick={() => handleClick(control)}></div>
))}
const handleClick = (control) => {
  control.background = 'green';
}  

Upvotes: 0

Dennis Vash
Dennis Vash

Reputation: 53884

Currently, your ref targets only the last item, you should target all your control items by making an array of refs.

let controls = [];

const makeControls = () => {
  for (let i = 1; i <= 9; i++) {
    controls.push({ active: false });
  }
  return controls;
};

makeControls();

const ControlPanel = () => {
  const status = useRef([]);

  const handleClick = index => {
    status.current[index].style.background = 'green';
  };

  return (
    <>
      {controls.map((control, i) => (
        <div
          ref={ref => (status.current[i] = ref)}
          style={{ background: `blue`, width: 100, height: 100 }}
          key={i}
          onClick={() => handleClick(i)}
        />
      ))}
    </>
  );
};

Edit distracted-snowflake-ngp4n

Upvotes: 1

Related Questions