Home > other >  Material UI: How to access a child component function?
Material UI: How to access a child component function?

Time:03-05

I'm using Material UI to create an application with React. I have a Dialog that allows the user to change a series of information about an object (see the code below). There are two different buttons. Each button refers to a different object. So, depending on which object the user wants to change, the onDialogOpen function is called with different parameters. Everything is working fine.

export default function CollectingFormInput() {

  const [dialogOpen, setDialogOpen] = useState(false);
  const [snackbarOpen, setSnackbarOpen] = useState(false);
  const [snackbarMessage, setSnackbarMessage] = useState('');
  const [name, setName] = useState("");
  const [alias, setAlias] = useState("");
  const [latitude, setLatitude] = useState("");
  const [longitude, setLongitude] = useState("");
  const [address, setAddress] = useState("");
  const [notes, setNotes] = useState("");

  const onDialogOpen = (info) => {
    setName(info.name);
    setAlias(info.alias);
    setLatitude(info.latitude);
    setLongitude(info.longitude);
    setAddress(info.address);
    setNotes(info.notes);

    setDialogOpen(true);
  };

  const onDialogClose = () => {
    setDialogOpen(false);
  };

  const onSnackbarClose = (e, reason) => {
    if (reason === 'clickaway') {
      return;
    }

    setSnackbarOpen(false);
    setSnackbarMessage('');
  };

  const onCreate = () => {
    setSnackbarOpen(true);
    setSnackbarMessage(`Dados de ${name} atualizados!`);
    onDialogClose();
  };

  return (
    <Fragment>
      <Button color="primary" onClick={()=> onDialogOpen({
        name: "JAF_01",
        alias: "",
        latitude: 0,
        longitude: 0,
        address: "",
        notes: ""
      })}>
        Botão 1
      </Button>
      <Button color="primary" onClick={()=> onDialogOpen({
        name: "JAF_02",
        alias: "",
        latitude: 0,
        longitude: 0,
        address: "",
        notes: ""
      })}>
        Botão 2
      </Button>
      <Dialog  open={dialogOpen} onClose={onDialogClose}>
        <DialogTitle>Editar informações da estação</DialogTitle>
        <DialogContent>
          <Grid container spacing={2}>
            <Grid item xs={12} sm={6}>
              <TextField
                  margin="normal"
                  id="station-name"
                  id="outlined-read-only-input"
                  label="Nome"
                  defaultValue={name}
                  size="small" fullWidth
                  InputProps={{
                    readOnly: true,
                  }}
              />
            </Grid>
            <Grid item xs={12} sm={6}>
              <TextField
                  margin="normal"
                  id="station-alias"
                  id="outlined-basic"
                  label="Apelido"
                  InputProps={{ name: 'alias' }}
                  onChange={field => setAlias(field.target.value)}
                  value={alias}
                  defaultValue=""
                  size="small" fullWidth
              />
            </Grid>
            <Grid item xs={12} sm={6}>
              <TextField
                  margin="normal"
                  id="station-latitude"
                  id="outlined-basic"
                  label="Latitude"
                  InputProps={{ name: 'latitude' }}
                  onChange={field => setLatitude(field.target.value)}
                  value={latitude}
                  defaultValue=""
                  size="small"
                  fullWidth
              />
            </Grid>
            <Grid item xs={12} sm={6}>
              <TextField
                  margin="normal"
                  id="station-longitude"
                  id="outlined-basic"
                  label="Longitude"
                  InputProps={{ name: 'longitude' }}
                  onChange={field => setLongitude(field.target.value)}
                  value={longitude}
                  defaultValue=""
                  size="small"
                  fullWidth
              />
            </Grid>
          </Grid>
          <TextField
              margin="normal"
              id="station-address"
              id="outlined-basic"
              label="Endereço"
              InputProps={{ name: 'address' }}
              onChange={field => setAddress(field.target.value)}
              value={address}
              defaultValue=""
              size="small"
              fullWidth
          />
          <TextField
              margin="normal"
              id="station-obs"
              id="outlined-multiline-flexible"
              multiline
              maxRows={4}
              label="Observações"
              InputProps={{ name: 'notes' }}
              onChange={field => setNotes(field.target.value)}
              value={notes}
              defaultValue=""
              size="small"
              fullWidth
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={onDialogClose} color="primary">Cancelar</Button>
          <Button
            variant="contained"
            onClick={onCreate}
            color="primary"
          >
            Salvar
          </Button>
        </DialogActions>
      </Dialog>
      <Snackbar
        open={snackbarOpen}
        message={snackbarMessage}
        onClose={onSnackbarClose}
        autoHideDuration={4000}
      />
    </Fragment>
  );
}

Now, I'd like to clean up the code and create a new CollectingFormInput component that is independent of the two buttons. So my code would be something like...

<Fragment>
      <Button color="primary" onClick={()=> onDialogOpen({
        name: "JAF_01",
        alias: "",
        latitude: 0,
        longitude: 0,
        address: "",
        notes: ""
      })}>
        Botão 1
      </Button>
      <Button color="primary" onClick={()=> onDialogOpen({
        name: "JAF_02",
        alias: "",
        latitude: 0,
        longitude: 0,
        address: "",
        notes: ""
      })}>
        Botão 2
      </Button>
      <CollectingFormInput />
<Fragment>

I think that onDialogOpen would have to belong to the CollectingFormInput component and be called by the parent component.

I searched but couldn't find a way to access onDialogOpen from the parent component. Can someone help me?

CodePudding user response:

You can use useImperativeHanlde hook and forwardRef hook to expose some methods in a child component to a parent component.

  1. Wrap the CollectingFormInput in a forwardRef. And use useImperativeHanlde hook to expose methods in it as below.
import { forwardRef, useImperativeHandle } from "react";

const CollectingFormInput = forwardRef((props, ref) => {
  ...
  ...
  ...

  const onDialogOpen = (info) => {
    setName(info.name);
    setAlias(info.alias);
    setLatitude(info.latitude);
    setLongitude(info.longitude);
    setAddress(info.address);
    setNotes(info.notes);

    setDialogOpen(true);
  };

  const onDialogClose = () => {
    setDialogOpen(false);
  };

  useImperativeHandle(ref, () => ({
    onDialogOpen: onDialogOpen,
    onDialogClose: onDialogClose
  }));

  ...
  ...

  return (
    ...
    ...
  );
});
  1. In the parent component create a ref and pass it to CollectingFormInput to get initialized with the functions you exposed there in the component. Call the methods after doing the null / undefined checking.
import { useRef } from "react";

const ParentComp = () => {
  const dialogHandleRef = useRef(null);

  return (
    <Fragment>
      <Button
        color="primary"
        onClick={() =>
          dialogHandleRef &&
          dialogHandleRef?.current?.onDialogOpen({
            name: "JAF_01",
            alias: "",
            latitude: 0,
            longitude: 0,
            address: "",
            notes: ""
          })
        }
      >
        Botão 1
      </Button>
      <Button
        color="primary"
        onClick={() =>
          dialogHandleRef &&
          dialogHandleRef?.current?.onDialogOpen({
            name: "JAF_02",
            alias: "",
            latitude: 0,
            longitude: 0,
            address: "",
            notes: ""
          })
        }
      >
        Botão 2
      </Button>
      <CollectingFormInput ref={dialogHandleRef} />
    </Fragment>
  );
};
  • Related