SweetTomato
SweetTomato

Reputation: 569

React Hook UseEffect with className

Is it not possible to update classNames on rerender?

const [selected, setSelectedState] = useState(true);

let className = "none";
useEffect(() => {
    className = "appointment-item " + (selected ? "selected" : "");
    console.log(className );
}, [selected] );


return (
  <div className={`${className}`} onClick={()=>setSelectedState(!selected)}>{Math.random()}</div>
);

className in the console shows correctly, but when the div rerenders (which i see because the random number updates), the className stays as "none". I am also confused because I thought useEffect runs immediately as well.. so it should never be "none" in the first place becuase it should get overwritten right away?

Upvotes: 3

Views: 7867

Answers (4)

johannchopin
johannchopin

Reputation: 14844

You can achieve that using a useState hook for the className variable if you want to use useEffect:

const [selected, setSelectedState] = useState(true);
const [className , setClassName] = useState("none");

useEffect(() => {
    setClassName("appointment-item " + (selected ? "selected" : ""))
    console.log(className );
}, [selected] );


return (
  <div className={className} onClick={()=>setSelectedState(!selected)}>{Math.random()}</div>
);

But I highly recommend @MarkoCen response.

Upvotes: 0

Zilva
Zilva

Reputation: 31

You don't need to have an useEffect for this.

all you need,

const [selected, setSelectedState] = useState(true);

let clsName = 'appointment-item ' + (selected ? 'selected' : '');

return(
<div className={`${clsName}`} onClick={() => setSelectedState(!selected)}>
{Math.random()}
</div>
)

Upvotes: 1

hjrshng
hjrshng

Reputation: 1826

To add more details on @MarkoCen answers (that is right), there are few things to know when working with hooks:

  • useEffect runs after each render.

  • On each render, the component re-executes from beginning, after the hooks definition. Given your code (I added comments to identity each cycle)

const [selected, setSelectedState] = useState(true); // 0

let className = "none"; // 1

useEffect(() => { 
    // 3
    className = "appointment-item " + (selected ? "selected" : "");
    console.log(className );
}, [selected] );

// 2
return (
  <div className={`${className}`} onClick={()=>setSelectedState(!selected)}>{Math.random()}</div>
);

Here are the cycles in order:

First render:

  • 0: useState

  • 1: className = none

  • 2: render with className = none

  • 3: useEffect

Re-renders:

  • 1: className = none

  • 2: re-render with className = none

  • 3: useEffect

As you can see, each render set className to "none" each time, so you will never have what you expect.

Upvotes: 6

MarkoCen
MarkoCen

Reputation: 2324

No need useEffect since selected already defined as a state, and you can compute the class name from selected

const [selected, setSelectedState] = useState(true);

const className = "appointment-item " + (selected ? "selected" : "");

return (
  <div className={className} />
)

Upvotes: 3

Related Questions