Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to set initial state with most up to date data returned from API?

I’m making a simple form to edit an app, the initial state of the name & description of the app is set using the data returned from the API.

Currently, when submitting the form the initial data seems to be logging as undefined, the name & description is being set as undefined which occurs in the first render (I have commented in the code where the logs are)

How can I make sure the initial state of name & description has the most up to date information?
Is the excessive renders the problem?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Thanks for taking a look, any help would be appreciated.

import React, { useState, useContext, useEffect } from "react";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import Container from "@material-ui/core/Container";
import SaveIcon from "@mui/icons-material/Save";
import CloseIcon from "@mui/icons-material/Close";
import { makeStyles } from "@material-ui/styles";
import TextField from "@material-ui/core/TextField";
import { Grid } from "@mui/material";
import { useDispatch } from "react-redux";
import { updateApp, updateSelectedApp } from "../../services/reducers/apps";
import { EndpointContext } from "../../baxios/EndpointProvider";
import { useParams } from "react-router-dom";


export default function EditApp() {
  const { appid } = useParams();

  const classes = useStyles();
  const dispatch = useDispatch();
  const endpoints = useContext(EndpointContext);

  const [selectedApp, setSelectedApp] = useState({});
  const [isLoaded, setIsLoaded] = useState(false); // <--- Is there anyway I can also remove this useState? without this the default values in the forms dont populate

  useEffect(() => {
    async function fetchApp() {
      await endpoints.appEndpoints.get(appid).then((response) => {
        if (response.status === 200) {
          setSelectedApp(response.data);
          setIsLoaded(true);
        }
      });
    }
    fetchApp();
  }, []);

  useEffect(() => {
    console.log(selectedApp);
  }, [selectedApp]);

  const [name, setName] = useState(selectedApp.name);
  const [description, setDescription] = useState(selectedApp.description);

  console.log("---", name, selectedApp.name); // <--- The page renders 3 times, each render log looks like this 
// 1st render - --- undefined, undefined
// 2nd render - --- undefined, Appname
// 3rd render - --- undefined, Appname


  const handleSubmit = (e) => {
    e.preventDefault();

    console.log("triggered", name, description); // <--- This logs (triggered, undefined, undefined)

    if (name && description) {
      const body = { name: name, description: description };
      endpoints.appEndpoints.put(selectedApp.id, body).then((response) => {
        if (response.status === 200) {
          dispatch(updateApp(response.data));
          setSelectedApp(response.data);
          setName(selectedApp.name);
          setDescription(selectedApp.description);
        }
      });
    }
  };

  return (
    <div style={{ margin: 100, marginLeft: 350 }}>
      {isLoaded ? (
        <Container size="sm" style={{ marginTop: 40 }}>
          <Typography
            variant="h6"
            color="textSecondary"
            component="h2"
            gutterBottom
          >
            Edit App
          </Typography>

          <form noValidate autoComplete="off" onSubmit={handleSubmit}>
            <TextField
              className={classes.field}
              onChange={(e) => setName(e.target.value)}
              label="App Name"
              variant="outlined"
              color="secondary"
              fullWidth
              required
              size="small"
              defaultValue={selectedApp.name}
              error={nameError}
            />
            <TextField
              className={classes.field}
              onChange={(e) => setDescription(e.target.value)}
              label="Description"
              variant="outlined"
              color="secondary"
              rows={4}
              fullWidth
              required
              size="small"
              defaultValue={selectedApp.description}
              error={descriptionError}
            />
            <Grid container spacing={2}>
              <Grid item>
                <Button
                  // onClick={handleSubmit}
                  type="submit"
                  color="primary"
                  variant="contained"
                  endIcon={<SaveIcon />}
                >
                  Save
                </Button>
              </Grid>
            </Grid>
          </form>
        </Container>
      ) : (
        <></>
      )}
    </div>
  );
}

>Solution :

When using const [name, setName] = useState(defaultName), if the defaultName is updated in a future render, then the name value will not be updated to the this latest value.

So in your case you can make the following changes :

  const [name, setName] = useState();
  const [description, setDescription] = useState();
  useEffect(() => {
    setName(selectedApp.name)
    setDescription(selectedApp.description)
  }, [selectedApp])
)

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading