6

I have a similar problem as How to focus a Material UI Textfield on button click? but I couldn't solve the problem with the answers provided.

Here's the code. Basically, all of the textfields are disabled. When edit profile button is clicked, it will set the disabled state for account name to false and I would like to focus on that particular field. I'm using the useRef hooks to reference out the field but it didn't work.It would only work if I click the button twice.

import React, { useState, useEffect, useRef } from "react";
import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";
import PermContactCalendarIcon from "@material-ui/icons/PermContactCalendar";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Paper from "@material-ui/core/Paper";

const useStyles = makeStyles((theme) => ({
  paper: {
    marginTop: theme.spacing(3),
    padding: theme.spacing(5),
    display: "flex",
    flexDirection: "column",
    alignItems: "center",
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: "100%", // Fix IE 11 issue.
    marginTop: theme.spacing(3),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
  fixedHeight: {
    height: 1000,
  },
  button: {
    marginTop: theme.spacing(3),
    marginRight: theme.spacing(3),
  },
}));

export default function AccountPage({ userdata }) {
  const classes = useStyles();

  const [accountName, setAccountName] = useState("");

  const [disabled, setDisabled] = useState(true);
  const inputRef = useRef(null);

  const handleOnChange = (e) => {
    if (e.target.name === "accountname") {
      setAccountName(e.target.value);
    }
  };

  return (
    <Container component="main">
      <CssBaseline />
      <Paper>
        <div className={classes.paper}>
          <Avatar className={classes.avatar}>
            <PermContactCalendarIcon />
          </Avatar>
          <Typography component="h1" variant="h5">
            Account & User Settings
          </Typography>
          <form className={classes.form} noValidate>
            <Grid
              container
              direction="column"
              justify="center"
              alignItems="stretch"
              spacing={2}
            >
              <Grid item xs={12}>
                <Typography color="primary" display="inline" noWrap>
                  Account Name :
                </Typography>
              </Grid>
              <Grid item xs={12}>
                <TextField
                  name="accountname"
                  variant="outlined"
                  fullWidth
                  //   autoFocus
                  onChange={handleOnChange}
                  disabled={disabled}
                  value={accountName}
                  inputRef={inputRef}
                />
              </Grid>
              <Grid item xs={12}>
                <Typography color="primary" display="inline" noWrap>
                  E-mail Address :
                </Typography>
              </Grid>
              <Grid item xs={12}>
                <TextField
                  variant="outlined"
                  fullWidth
                  value={userdata.email}
                  type="email"
                  name="email"
                  disabled={true}
                  //   onChange={handleOnChange}
                />
              </Grid>
              <Grid item xs={12}>
                <Typography color="primary" display="inline" noWrap>
                  Account ID :
                </Typography>
              </Grid>
              <Grid item xs={12}>
                <TextField
                  variant="outlined"
                  fullWidth
                  name="id"
                  value={userdata._id}
                  type="text"
                  disabled={true}
                  //   onChange={handleOnChange}
                />
              </Grid>
            </Grid>
            <Button
              variant="contained"
              color="primary"
              className={(classes.submit, classes.button)}
              onClick={() => {
                setDisabled(false);
                inputRef.current.focus();
              }}
            >
              Edit Profile
            </Button>
            <Button
              //   type="submit"
              variant="contained"
              color="primary"
              className={(classes.submit, classes.button)}
              onClick={() => {
                setDisabled(true);
              }}
            >
              Save Changes
            </Button>
          </form>
        </div>
      </Paper>
    </Container>
  );
}

1 Answers1

8

The problem is in the onClick event handler of the Edit button:

() => {
  setDisabled(false);
  inputRef.current.focus();
};

You should keep in mind that setState is asynchronous. Here is a quote from the React docs:

The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.

In other words, React promises you disabled will be set to false sometime in the future, but probably not when the line inputRef.current.focus() is executed.

So what you are trying to do is to ask React: when disabled is set to false, focus an element. And this is what useEffect hook is for - it lets you perform side effects. So instead of putting inputRef.current.focus() right after setDisabled(false), you can set up an useEffect hook as follow:

useEffect(() => {
  if (!disabled) {
    inputRef.current.focus();
  }
}, [disabled]); // Only run the function if disabled changes
hangindev.com
  • 4,573
  • 12
  • 28
  • Thanks for your help again! It works now but I'm still confused when to use the useEffect hook. So basically, if we have more than 1 function , it's better to go for useEffect as not to interfere with the main function? – Danial Asraf Norbee Jun 13 '20 at 13:36
  • When some actions need to be done as a result of state change, you should consider using `useEffect`. Those actions can be fetching an API, changing document.title or focusing an element, etc. I recommend reading the React docs about [Using the Effect Hook](https://reactjs.org/docs/hooks-effect.html). – hangindev.com Jun 13 '20 at 13:42