0

I want to change the state of setArray but I cant change it. The console.log shows empty array. The other states are working but not this I've used useEffect too but nothing is happening. Kindly give me a solution.

import React from 'react';

import { Link} from 'react-router-dom'

import {useEffect} from 'react';

const Register = () =>{

   const classes = useStyle();
   const [name, setName] = React.useState('');
   const [password, setPassword] = React.useState('');
   const [array, setArray] = React.useState([])



   const submit = (event) =>{
       const obj = {}
       event.preventDefault();
       if(array.length === 0){
          localStorage.setItem('name', name);
          localStorage.setItem('password',password);
          localStorage.setItem('array',JSON.stringify(array))
          obj.id = Math.random();
          obj.name = localStorage.getItem('name');
          obj.password = localStorage.getItem('password');
          setArray(array => [...array,obj])
          console.log(array);
          return alert('You are registered'); 
       }
    }

 return(
    <div>
        <div className = {classes.formWrapper}>
            <Paper elevation={3} className = {classes.paper} >
            <Typography variant="h5" style = {{ textAlign : 'center'}}>Register</Typography>
            <form noValidate autoComplete="off">
                <TextField id="username" className={classes.textfield} style={{width : '95%'}} value = {name} name = "username"  label="Username" onChange = {e=>setName(e.target.value)} />
                <br />
                <TextField id="password" className={classes.textfield} style={{width : '95%'}} value = {password} name = "password"  label="Password" onChange = {e=>setPassword(e.target.value)} />
                <br />
                <br />
                <Link to='/'>SignIn</Link>
                <Button variant="contained" color="secondary" style = {{width : '100%'}} onClick = {submit} >Register </Button>
            </form>
            </Paper>
        </div>
    </div>

  )
}

export default Register;
1
  • 1
    You are trying to access the state through console.log( ), just after calling setState( ) . The setState is asynchronus in nature and thus it would give you previous or initialState ,if you to try to print it in console. Commented Jun 29, 2021 at 12:27

2 Answers 2

2

To clarify, the reason why console.log(array); logs the old array is not because setArray (or rather, it's implementation) is or is not asynchronous. It is because, simply put, it's impossible. And even if it were fully synchronous, it would still be so.


The reason:

  1. Javascript is a pass-by-value (or rather call-by-sharing), or more importantly not a pass-by-reference language. This means, that there is absolutely no way to implement setArray in such a way that it could change what is assigned to array. A dead giveaway that you shouldn't even assume this was possible, is that array is a const.

  2. Except from throwing an exception, it is impossible to prevent the execution of the remaining code after setArray(array => [...array,obj]) from inside setArray.

Also note, that this is not a react thing. It's simply a part of the language as is. So there's also no way to fix this (not that I'm saying a fix is needed).


So what is really happening? Quit simple:

const submit = (event) => { ... } forms a closure over the constant array. And thus console.log(array); must refer to that value. That setArray(array => [...array,obj]) stages a state update for the next render, and whether that process is synchronous or asynchronous, is completely irrelevant here.


So how to refer to a changed state variable?

  1. use useEffect appropriately.

  2. use the value you would want to assign as the new state where you calcualte it. E.g.:

    setArray(array => {
        const next = [...array, obj];
    
        console.log(next); // new value
    
        return next;
    });
    
    console.log(array); // *still* the old value, for the above mentioned reasons
    
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for this writeup. I hope OP accepts this as 'accepted answer'
@PiyushMahapatra You're welcome! Please share the knowledge! This type of question get's asked so much, and falsely answered so often...
0

setState is asynchronous. It means you can’t call it on one line and assume the state has changed on the next.

From React Docs.

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

4 Comments

Awaiting a function that has void as it's return type makes absolutely no sense. Also it's irrelevant that the state update might or might not be asynchronous. The described problem is simply a case of a stale closure.
Can you elaborate this with an answer? By looking at the description of the question,it is clear that OP is concerned about the inability to log the state,just after mutating it with setState( ). In React docs it is clearly mentioned why it happens and there is a github thread too. github.com/facebook/react/issues/11527 Additionally,I tried to give a small code example to achieve what he want to. Thanks.
I've posted my answer. I hope it helps.
If you are adamant to log the state after the setState,then try this(just to experiment,dont do in prod app): js async logAftersetState(){ await setState(array => [...array,obj]); console.log(array); } // Call logAfterSetState() inside your submit function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.