Material UI: 如何访问子组件函数?

Material UI: How to access a child component function?

我正在使用 Material UI 创建带有 React 的应用程序。我有一个 Dialog 允许用户更改有关对象的一系列信息(请参见下面的代码)。有两个不同的按钮。每个按钮指向一个不同的对象。因此,根据用户想要更改的对象,使用不同的参数调用 onDialogOpen 函数。一切正常。

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>
  );
}

现在,我想清理代码并创建一个独立于这两个按钮的新 CollectingFormInput 组件。所以我的代码会是这样的...

<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>

我认为onDialogOpen必须属于CollectingFormInput组件并被父组件调用。

我进行了搜索,但找不到从父组件访问 onDialogOpen 的方法。有人可以帮助我吗?

您可以使用 useImperativeHanlde hook and forwardRef 钩子将子组件中的一些方法暴露给父组件。

  1. CollectingFormInput 包裹在 forwardRef 中。并使用 useImperativeHanlde 钩子来公开其中的方法,如下所示。
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. 在父组件中创建一个 ref 并将其传递给 CollectingFormInput 以使用您在组件中公开的函数进行初始化。在执行 null / undefined 检查后调用方法。
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>
  );
};