1

Before I ask my question: Yes I did read every other article about this but im basicly just stuck and can't figure out how to fix this.

So basically my rendering and console.log are one step behind my useState changes. The biggest problem visually and functionally is the dynamic button which doesn't enable/disable correctly. My code is:

import React, { useState, useEffect } from 'react';

import ApiCalls from "../../classes/apiCallsClass"
import useStyles from "./styles";
import { TextField, Select, Paper, Grid, MenuItem, Button } from '@material-ui/core';
import { withStyles } from "@material-ui/styles";

export default function TicketCreation() {
  const [buttonDisabled, setButtonDisabled] = useState(true);
  const [, setTick] = useState(0);

  const classes = useStyles();
  const [ticketname, setTicketName] = useState('');
  const [description, setDescription] = useState('');
  const [prio, setPrio] = useState('');
  const [system, setSystem] = useState('');


  const handleChangePrio = event => {
    setPrio(event.target.value);
    checkFormFilled();
  };

  const handleChangeDescription = event => {
    setDescription(event.target.value);
    checkFormFilled();
  };

  const handleChangeName = event => {
    setTicketName(event.target.value);
    checkFormFilled();
  };

  const handleChangeSystem = event => {
    setSystem(event.target.value);
    checkFormFilled();
  };

  const checkFormFilled = () => {
    if (ticketname && description && prio && system) {
      setButtonDisabled(false, function(){
        setTick(tick => tick + 1);
      });  
    } else{
      setButtonDisabled(true, function(){
        setTick(tick => tick + 1);
      }); 
    }
  }

  const handelButtonSubmit = event => {
    if (ApiCalls.createTicket(ticketname, system, prio, description)) {
      console.log("Created");
    }
  }

  return (
    <div className={classes.root}>
      <Grid container spacing={3}>
        <Grid item xs={4}>
          <Paper className={classes.paper}>
            <TextField id="standard-basic" value={ticketname} onChange={handleChangeName} label="Ticketname" fullWidth />
          </Paper>
        </Grid>
        <Grid item xs={4}>
          <Paper className={classes.paper}>
            <Select value={system} onChange={handleChangeSystem} displayEmpty fullWidth className={classes.selectEmpty}>
              <MenuItem value="" disabled>
                System
              </MenuItem>
              <MenuItem value={1}>Radar</MenuItem>
              <MenuItem value={2}>Missles</MenuItem>
              <MenuItem value={3}>Toilet</MenuItem>
            </Select>
          </Paper>
        </Grid>
        <Grid item xs={4}>
          <Paper className={classes.paper}>
            <Select value={prio} onChange={handleChangePrio} displayEmpty fullWidth className={classes.selectEmpty}>
              <MenuItem value="" disabled>
                Priority
              </MenuItem>
              <MenuItem value={1}>Low</MenuItem>
              <MenuItem value={2}>Medium</MenuItem>
              <MenuItem value={3}>High</MenuItem>
            </Select>
          </Paper>
        </Grid>
        <Grid item xs={12}>
          <Paper className={classes.paper}>
            <TextField
              value={description}
              onChange={handleChangeDescription}
              id="outlined-multiline-static"
              label="Ticket description"
              multiline
              rows="4"
              variant="outlined"
              fullWidth />
          </Paper>
        </Grid>
        <Grid item xs={12}>
          <Button  disabled={buttonDisabled} onClick={handelButtonSubmit} variant="contained" color="primary">
            Create
          </Button>
        </Grid>
      </Grid>
    </div>
  );
}

Don't mind the setTick usestate, I tried this in combination with a callback to fix the issue but it wasn't working.

My question basically is: What do I need to do to enable my button instantly without delay? (and maybe other things I do wrong? first time using react hooks)

Norstan
  • 39
  • 4
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Emile Bergeron Dec 19 '19 at 21:04

1 Answers1

3

State setters (setSystem, etc.) are asynchronous. That means where you're doing things like setSystem(event.target.value); checkFormFilled() -- the new value of system, in this case, may not be updated by the time checkFormFilled runs. That's likely why you're seeing things "lag behind" the values.

What you should be doing is not calling checkFormFilled directly after your set state calls; instead, put checkFormFilled in a useEffect hook that depends on the form elements. That way, React will call your checkFormFilled function when any of those values are actually updated.

useEffect(() => {
    checkFormFilled();
}, [ticketname, description, prio, system]);

Then your change handlers only need to worry about setting the values, not calling the form filled check, and everything is guaranteed to be up to date.

IceMetalPunk
  • 5,476
  • 3
  • 19
  • 26
  • 1
    It works now. I didn't know that useEffect was also used for these type of things, thanks for the help! – Norstan Dec 20 '19 at 20:04
  • Yep :) useEffect will run its callback once when the component mounts, and then again every time any of its listed dependent variables changes. (If you don't list any dependencies, it will only run once when mounted and never again, which can also be useful.) – IceMetalPunk Dec 30 '19 at 18:04