Search code examples
javascriptreactjsreact-hooksinitializationuse-state

React Hook useState value being reset to initial value


The state of a value set using React useState hook gets set to the proper value and then reset to null. Critical code below. The click event that sets the startDate to the current date and time is 3 components down from where startDate is initialized. When setStartDate did not work I created an arrow function, updateStartDate. Both had the same problem where the startDate was changed after the click event (witnessed per the console.log in the top component), but was null just before the next click event (per the console.log in the click event). This is not an async problem as I see the change made before subsequent click.

If this is something that just does not work please explain. I could probably fix with useReducer but prefer to keep the useState if there is something I can do to correct this... If not correctable then I would like to at least understand why it does not work so that I can avoid this problem in the future.

export const DisplayTicTacToeContainer = (props) => {
 const [startDate, setStartDate]= useState();
 const updateStartDate = (newDate) => {
   setStartDate(newDate);
 }
 useEffect (() => {
   setStartDate(null);
 }, []);

 useEffect(() => {
    console.log( "displayTicTacToeContainer useEffect for change of startDate = ", startDate)
  }, [startDate]);

 return (
      <DisplayTicTacToeMatch  arrayOfMatchingItems ={arrayOfMatchingItems}
        startDate={startDate} 
        setStartDate={setStartDate}
        updateStartDate={updateStartDate}          
      />);
}

//-----------------------------------------------
export const DisplayTicTacToeMatch = (props)  => {
 const   { startDate,
           setStartDate,
           updateStartDate,
           } = props;

 useEffect(() => {
  // Performs some prep and working fine.
 }, []);

 return (
    <TicTacToe
      startDate={startDate} 
      setStartDate={setStartDate}
      updateStartDate={updateStartDate}
    />
  );
}
//-----------------------------------------------
const TicTacToeContainer = (props) => {
 const { startDate,
         setStartDate,
         updateStartDate,
        } = props;

 const [board, setBoard] = useState(<Board 
     updateStartDate={updateStartDate}
     startDate={startDate} 
     setStartDate={setStartDate}/>);

 return (
        <Board/>
    )
}
export default TicTacToeContainer;

I renamed the component to BoardComponent and the state variable to boardLayout. I included the full return portion of the BoardComponent below.

As I am still experiencing the problem I would agree with you that, "DisplayTicTacToeContainer is being mounted twice". Any thoughts on how I can avoid this from happening?

Other than this inability to setStartDate, everything is working fine.

//-----------------------------------------------
const Board = (props) => {
 const { updateStartDate,
         startDate,
         setStartDate,
    } = props;

    return (
        <>
        <Grid container maxwidth="lg" alignItems="center" spacing={1}>
            <Grid item xs={9}>
                <Grid container alignItems="center">
                    <Grid item xs={9}>
                        <Typography variant = "body1">                      
                        First select a square.  Once the "Inquiry" word or phrase appears below, find 
                        the correct response in the column on the right and select that buttton.  A correct
                        response will fill the square previously selected with an "O" or "X".
                        </Typography>
                        <div style={{ width: '100%' }}>
                    <Box
                        display="flex"
                        flexWrap="wrap"
                        p={1}
                        m={1}
                        bgcolor="background.paper"
                        css={{ maxWidth: 900 }}
                    >
                    <Box p={1} bgcolor="grey.300">
                        Inquiry : {inquiry}
                    </Box>
                    </Box>
                    <Box
                        display="flex"
                        flexWrap="wrap"
                        p={1}
                        m={1}
                        bgcolor="background.paper"
                        css={{ maxWidth: 900 }}
                    >
                    <Box p={1} bgcolor="grey.300">
                        Next move by : {currentPlayer}
                    </Box>

                    <Box p={1} bgcolor="grey.300">
                        {showStatus}
                    </Box>
                    </Box>
                    </div>
                    </Grid>

                </Grid>
                        <MyAux>
                            {boardLayout.map((row, rowId) => { 
            const columns = row.map((column, columnId) => (
    
                <Grid key={columnId} item>
                    <ButtonBase > 
                        <Paper
                            onClick={(e) => {
                                clickSquareHandler(e);
                                }}
                            elevation={4}
                            data-coord={rowId + ':' + columnId}
                            id={"Square" + rowId.toString() +  columnId.toString()}
                            className={classes.Paper}>
                            <Icon
                                className={classes.Icon}
                                style={{fontSize: 78}}>
                            </Icon>
                        </Paper>
                    </ButtonBase>
                </Grid>
                ));
                return (
                <Grid
                    key={rowId}
                    className={classes.Grid}
                    container
                    spacing={2}>
                    {columns}

                </Grid>)
           })}
                        </MyAux>
                        </Grid>

        <Grid item xs={3} >
        <Paper className={classes.paper}>
        <Typography variant = "body1">    
            Response Options
        </Typography>
            <ButtonGroup
            orientation="vertical"
            color="secondary"
            aria-label="vertical outlined secondary button group"
        >
            {responseChoices.map((choice) => (
                              
                <Controls.Button
                    key ={choice.value}  
                    text={choice.value}
                    variant="contained"
                    color = "secondary"
                    onClick={() => {
                    chooseChecker(choice);
                    }}
                    className={
                    response && response.value === choice.value ? "selected" : ""
                    }
                    disabled={!!selected[choice.value]}
                    fullWidth = "true"
                    size = "small"
                    />
                ))}
            </ButtonGroup>
            </Paper>
            </Grid>
            </Grid>
                </>
    )
}

BoardContainer.propTypes = {
    won: PropTypes.func,
    size: PropTypes.number
};

export default BoardContainer;


Solution

  • At least, code below doesn't make much sense. Please don't set state value as a component. Also, try to name state variable different from components, since it will confuse you at some ppint.

     const [board, setBoard] = useState(<Board 
         updateStartDate={updateStartDate}
         startDate={startDate} 
         setStartDate={setStartDate}/>);
    
     return (
            <Board/>
        )
    

    Another possibility is that the DisplayTicTacToeContainer is being mounted twice, but I can't confirm it with the code provided.