1
  1. In the code below, how do I make the value of status variable change immediately after setStatus(!status)? I understand that state updates in React are batched, therefore I am using the useEffect with callback, but still the value of status is logged as false upon first button click (as if it did not change immediately).
  2. Considering that the app rendering is not in any way dependent on the status variable (actually, I would even prefer that the changes of this variable do not trigger a render), would it be a bad practice to not use useEffect at all and make status a "normal" variable instead, assigned like status = true?

function App() {

  const [status, setStatus] = React.useState(false)

  React.useEffect(() => toggleStatus, [status])

  function onClickHandler() {
    toggleStatus()
    console.log(status)
  }

  function toggleStatus() {
    setStatus(!status)
  }

  return ( 
    <div>
        <button onClick={onClickHandler}>Toggle & log status</button>
    </div>
  )
}

ReactDOM.render( < App /> , document.getElementById('root'))
<script src="https://unpkg.com/react@^16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.13.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
<div id="root"></div>
barciewicz
  • 3,511
  • 6
  • 32
  • 72
  • PLease check this https://brewhouse.io/blog/2015/03/24/best-practices-for-component-state-in-reactjs.html . For understanding when to use the state and when not – Harmandeep Singh Kalsi Jul 07 '20 at 14:56
  • Whenever you change the value of status with setStatus the component will render, that's exactly the role of useState() hook; – vitomadio Jul 07 '20 at 15:04

4 Answers4

1
  1. When you click onClickHandler triggered ==> toggleStatus and console.log(status) are triggered synchronously (setStatus(!status) of toggleStatus is triggered asynchronously. So you see false is logged. AND you are using useEffect incorrectly. React.useEffect(() => toggleStatus, [status]) mean the first argument of useEffect return toggleStatus function, that is the cleanup action of useEffect

Try this to see status logged

React.useEffect(() =>{
  //you can see true after first click here
  console.log(status)
}, [status])
  1. If you want to change status immediately and don't re-render the component, you would use useRef instead
Tony Nguyen
  • 3,298
  • 11
  • 19
1

I will comment your code

function App() {

  const [status, setStatus] = React.useState(false)

  React.useEffect(() => toggleStatus, [status]) // here you are toggle the status again when the status change 

  function onClickHandler() {
    toggleStatus()
    console.log(status)
  }

  function toggleStatus() {
    setStatus(!status) // here you can do setStatus(prevStatus => !prevStatus)
  }

  return ( 
    <div>
        <button onClick={onClickHandler}>Toggle & log status</button>
    </div>
  )
}

ReactDOM.render( < App /> , document.getElementById('root'))
<script src="https://unpkg.com/react@^16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.13.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
<div id="root"></div>

You can try with a console.log(status) inside the useEffect and see if everything its ok.

React.useEffect(() => console.log(status), [status])

Now talking about the second point, in my opinion that is not a bad practice. According to React docs:

The state contains data specific to this component that may change over time. [...] If you don't use it in render(), it shouldn't be in the state. For example, you can put timer IDs directly on the instance.

Nacho Zullo
  • 551
  • 3
  • 5
1

React states are used to track application state and re-render the app, when this underlying state changes.

Now, if you do not want a re-render, when status changes, you should declare status as a regular variable.

You can still use the useEffect react hook, if you want. And you can initialize status here. useEffect is called the component is mounted/rendered on the browser DOM

Faraaz Malak
  • 329
  • 1
  • 6
0

In the code below, how do I make the value of status variable change immediately after setStatus(!status)?

You can't make state to change immediately as changing state is async.

I would even prefer that the changes of this variable do not trigger a render), would it be bad practice to not use useEffect at all and make status a "normal" variable instead, assigned like status = true?

First, check what are the use cases for useEffect.

About making a "normal" variable instead, its indeed a bad practice:

const Component = () => {
   let variable = ...;
   return <>...</>
}

That's because, on every render, such variables will re-assigned (because the body of the function component executed on every render). Usually, it is not the desired behavior.

Instead, you want to use a reference with useRef. See what is the difference between useRef and a variable.

Changing the reference value won't trigger a render, therefore won't update the UI.

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118