Say that you have a <Button>
and <Dialog>
component. On button click, the dialog should open (open
prop). Normally you would put both components togheter, using react useState
to control the state:
export const FilterDialog() {
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<Button onClick={handleOpen}
Open
</Button>
<Dialog open={open}>
<Button onClick={handleClose}
Close
</Button>
</Dialog>
);
};
This isn't a good solution as other part of the application may open the FilterDialog
.
Thr problem could easly solved using a state pattern where state is global and the Dialog is attached to the isFilterDialogOpen
state slice.
Is there any way to avoid the use of the global state pattern and have multiple button triggers controlling the dialog?
CodePudding user response:
If it's a component communicating to another component then the approaches available are React.Context, Redux state, just a parent state that's encapsulating those two components (in which will look like a global state) or via useRef. I'm not sure what issue you wanna avoid here but it looks somewhat okay. Happy to be corrected here.
About other parts of the app that may open it. We can create separate components (different dialog types) but if it's just one instance, we can just close and reopen the Dialog to re-render it.
CodePudding user response:
Just pass in open
and handleClose
as props
export const FilterDialog = ({open, onClose}) => {
return (
<Dialog open={open}>
<Button onClick={onClose}>Close</Button>
</Dialog>
);
};
export const App = () => {
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<>
<Button onClick={handleOpen}>Open</Button>
<FilterDialog open={open} onClose={handleClose} />
</>
);
}
This is a common pattern to pass in callback functions that get executed by dialog buttons. Usually you'll have an onConfirm
or something as well. You can reuse the same dialog to do different things.
For example, let's say this dialog lets the user input a filter string, and you want to filter an array from a different component when the user presses ok. You can pass in a function that takes that string as a parameter.
export const FilterDialog = ({open, onClose, onOk}) => {
const [value, setValue] = useState("")
return (
<Dialog open={open}>
<input value={value} onChange={(e) => setValue(e.target.value)}/>
<Button onClick={() => onOk(value)}>OK</Button>
<Button onClick={onClose}>Close</Button>
</Dialog>
);
};
export const App = () => {
const [open, setOpen] = useState(false);
const [arr, setArr] = useState(["apple", "banana", "orange"]);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const handleFilter = (filterString) => {
console.log(arr.filter((fruit) => fruit.startsWith(filterString)));
setOpen(false);
};
return (
<>
<Button onClick={handleOpen}>Open</Button>
<FilterDialog open={open} onClose={handleClose} onOk={handleFilter} />
</>
);
}