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.
- Wrap the
CollectingFormInput
in aforwardRef
. And useuseImperativeHanlde
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 (
...
...
);
});
- 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>
);
};