I am creating a react website in which many pages have a specific button that should look the same for all. Should this button be its own component? If so, how would I specify the onClick events to be different for each button if it is a component?
Yes, it should be its own component.
Create it in a separate file so you can import them where you need to use.
The component should receive a onClick prop that you pass to the internal button tag.
See this example: https://reactjs.org/docs/components-and-props.html#composing-components
export const Button = ({ label, onClick, disabled }) => {
return (
<button
onClick={onClick}
disabled={disabled}
>
{label}
</button>
)
}
and then you can export this component inside any of the components you want, and pass in values like onClick and label to make it more dynamic
export const DemoFunction () => {
const onClickHandler = () => {}
return (
<Button label="Click" onClick={onClickHandler} />
)
}
Related
I'm trying to create rich React component as popover content.
If I use example with simple const popover (https://react-bootstrap.netlify.app/components/overlays/#examples-1) everything works fine.
Problem
But custom react component fails to position itself. It appears on top left of the screen
const MyPopover = React.forwardRef((props, ref) => {
return <Popover ref={ref} placement={"bottom"}>
<Popover.Header as="h3">
<Form.Control></Form.Control>
</Popover.Header>
<Popover.Body>
<strong>Holy guacamole!</strong> Check this info.
</Popover.Body>
</Popover>
})
const PopoverChooser = ({children, container}) => {
const _refTarget = useRef(null)
return <>
<OverlayTrigger
trigger="click"
placement="bottom"
overlay={<MyPopover ref={_refTarget}/>}
target={_refTarget.current}
>
{children}
</OverlayTrigger>
</>
}
export default PopoverChooser;
As you can see, I'v tried to use ref's, but it's doesn't help.
Question
How can it link popover to target button (in image as dropdown button and in code as {children}).
Or should I position MyPopover manually (by checking button ID and assigning position.. etc.?)
Completely different approach to dig in..?
Your approach to forward the ref was right. What you actually forgot is to also inject props. According to the documentation:
The and components do not position themselves.
Instead the (or ) components, inject ref and
style props.
https://react-bootstrap.netlify.app/components/overlays/#overview
So what you need to do is to spread the props like this:
const MyPopover = React.forwardRef((props, ref) => {
return (
<Popover ref={ref} {...props}>
https://codesandbox.io/s/trusting-sid-0050g9
In some components i am using recoil atoms to manage my states. One example is my modal component. It look something like this:
export const modalState = atom({
key: "modalState",
default: false
})
export const useToggleModalState = () => {
const setModalState = useSetRecoilState(modalState)
return (state, callback) => {
setModalState(state)
if (callback) {
callback()
}
}
}
export const Modal = (props) => {
<Transition show={modalState}>
<Dialog>
<Dialog.Title>My Modal Headline</Dialog.title>
<Dialog.Description>My Modal Description</Dialog.Description>
</Dialog>
</Transition>
}
and i am using this modal like this:
const toggleModalState = useToggleModalState();
return (
<Modal />
<Button text="Close Modal" onClick={() => toggleModalState(false)} />
)
however, if I use the modal multiple times, the modal is automatically duplicated, but I still use only one state for all modals. Of course, I don't want that. If I use a component multiple times, I want the state to be created multiple times, so that I can change the state of each component individually.
I have read that there are also atomFamilys. Could I use these at this point? What should my code look like then? Can multiple atoms also be created automatically if I use a component multiple times?
Why do you want to use recoil for that? The state of the modal is tied to the modal itself, it doesn't need to access global state.
You can just use useState to determine if you want to show a modal within a component:
export const Modal = (props) => {
<Transition show={props.show}>
<Dialog>
<Dialog.Title>My Modal Headline</Dialog.title>
<Dialog.Description>My Modal Description</Dialog.Description>
</Dialog>
</Transition>
}
export const ComponentWithModal = () => {
const [showModal, setShowModal] = useState(false);
return (
<>
<Modal show={showModal}/>
<Button text="Open Modal" onClick={() => setShowModal(true)} />
<Button text="Close Modal" onClick={() => setShowModal(false)} />
</>
)
}
I'm using Material UI for a button and want to render a different functional component onClick of a button.
I'm doing this all in a functional component as well. ( is the component I want to trigger)
const driveAction=props =>{
return <SharedDriveAction/>;
}
const Vehicle = ({vehicle}) => {
const classes = useStyles();
return (
<Button
onClick= {() => { driveAction }}
size="small"
color="secondary"
className={classes.button}
>
Drive
</Button>
);
}
export default Vehicle;
It looks like you want to leverage some state to conditionally render the button.
If you want to control whether or not the button is visible based on a click action, keep track of which component should render in state, then toggle that state with the onClick handler.
If what I am suspecting is true, then this should do the trick for you.
const Vehicle = ({vehicle}) => {
const classes = useStyles();
// Controls whether the drive action is rendered or not
const [showDriveAction, setShowDriveAction] = React.useState(false)
return showDriveAction ?
<SharedDriveAction/> :
<Button
onClick= {() => setShowDriveAction(true)}
size="small"
color="secondary"
className={classes.button}
>
Drive
</Button>
}
export default Vehicle;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You can use conditional render with react state.
and if you want to toggle use Something like that:
const Vehicle = ({ vehicle }) => {
const classes = useStyles();
const [showDrive, setShowDrive] = React.useState(false)
return (
<div>
{showDriveAction ?
<SharedDriveAction/> :
<Button
onClick= {() => setShowDriveAction(!showDriveAction)}
size="small"
color="secondary"
className={classes.button}
>
Drive
</Button>}
</div>
)
}
More simply speaking, have your overarching component have a state variable of open for the conditional render. OnClick, hook this.setState (for class components) to toggle the open variable. Inline conditional rendering can be handled quite easily in the below example.
{ props.open ? <Component/> : null }
How to call a Material UI Dialog during onClick on delete icon ie onClick={deletePlayer(id)} ?
I have added the Dialog.js under modal/Dialog and imported to Home component.
I have added a demo here
Short answer: Forked CodeSandbox with working dialog
Long answer:
First of all, you need to move the display/dismiss logic out of the AlertDialog component and into the component that actually triggers the display of the modal (in your case, the Home component). This means that you'll receive the open state and onClose handler as props (along with the playerId which will hold the ID of the player being targeted for deletion). So the signature of your dialog component becomes:
export default function AlertDialog({ open, onClose, playerId }) {
return (
<Dialog open={open} onClose={onClose} ...> ... </Dialog>
);
}
In Home, we add the logic to track and set the state of both the dialog open/closed status, and the ID of the player targeted for deletion. We do this through useState:
const [deleteDialog, setDeleteDialog] = useState(false);
const [playerId, setPlayerId] = useState("");
While you could have as many AlertDialog components as you have players by adding <AlertDialog /> inside your player map loop, it is redundant as you'll only ever have one modal active (by definition). So all you have to do is place a single instance of <AlertDialog /> in your Home component. A good convention is to place it before the closing encompassing tag:
return (
<div className="App">
.
.
.
<AlertDialog
open={deleteDialog}
onClose={() => setDeleteDialog(false)}
playerId={playerId}
/>
</div>
);
Finally, we deal with the handler responsible for displaying the modal, in your case deletePlayer. We have two things to do there: set the player ID targeted for deletion through the playerId state variable, and display the modal through the deleteDialog state variable:
const deletePlayer = id => e => {
setPlayerId(id);
setDeleteDialog(true);
};
Create a state in the Home component to handle the Dialog visibility, set the state on click and render the AlertDialog conditionally:
const [openDialog, setOpenDialog] = useState(false);
...
const deletePlayer = id => e => {
setOpenDialog(true);
};
...
return(
...
{openDialog && (
<AlertDialog isOpen={openDialog} setIsOpen={setOpenDialog} />
)}
Then in the AlertDialog component:
export default function AlertDialog(props) {
const { isOpen, setIsOpen } = props;
const handleClose = () => {
setIsOpen(false);
};
return (
<div>
<Dialog
open={isOpen}
onClose={handleClose}
...
Working Example:
I'm learning react so I'm building a weight tracker.
I have different pages where i ask some datas. So i want to develop a modal form.
I have already have a Modal component from Ionic.
So i builded a ModalForm with an header with close, a cancel and a ok button.
Inside the content i render props.childrens.
Something like that
<App>
<ModalForm>
<Input>
</ModalForm>
</App>
On pressing "Ok" the component will give the input value to the parent via callback.
That value will be validated ( so i cant give the value onChange).
But it will need to know the values of childrens input.
Moreover the parent will have control of inputs ( and validation ), that is not a thing that i like.
I can let the modal choose what inputs render with an internal switch, but it cant be reused for other porpuse.
Should abandon childrens and found another way ?
Please give me some advice on how composite my components to achieve this results.
Thank you
I've found a way.
I have a parent component, that is like a wrapper or a decorator, but is lower than my final component.
interface ModalProps {
title: string,
show: boolean,
setShow: Function,
value: number | string,
onSave: Function
}
const ModalInput: React.FC<ModalProps> = (props) => {
var { show, setShow, title, value, onSave } = props;
return (
<IonPopover isOpen={show} onDidDismiss={() => { setShow(false); }}>
<IonContent class="ion-text-center modal-content">
<IonCard>
<IonCardHeader>
<IonCardTitle>{title}</IonCardTitle>
</IonCardHeader>
<IonCardContent className="text-center">
{props.children}
</IonCardContent>
</IonCard>
<IonFooter>
<IonButton color="light" onClick={() => { setShow(false); }}>Cancel</IonButton>
<IonButton color="primary" onClick={() => { onSave(value); setShow(false); }}><IonIcon slot="start" icon={save} /> Save</IonButton>
</IonFooter>
</IonContent>
</IonPopover>
);
};
export default ModalInput;
It tooks the props to open/close the modal, a title, one props to get the child value and onSave that is a callback from app.
Then i wrote a more higher component with the implementation of the children.
All the props goes to the ModalInput wrapper.
interface InputProps {
onSave: Function,
show: boolean,
setShow: Function,
defaultValue: number
}
const WeightInput: React.FC<InputProps> = ({show, setShow, defaultValue, onSave}) => {
const [value, setValue] = useState<number>(defaultValue);
return (
<ModalInput show={show} setShow={setShow} title="Starting weight" value={value} onSave={onSave}>
<IonItem>
<IonInput value={value} onIonChange={e => setValue(parseFloat(e.detail.value!))}></IonInput>
</IonItem>
</ModalInput>
);
}
export default WeightInput;
And finally how to use it :
<WeightInput show={showWeight} setShow={setShowWeight} defaultValue={weight} onSave={(w:number) => setWeight(w)}/>
So it works like that :
on input change, the input call setValue for changing the state
on state change will change also the props for the modal component
on OK click the modal component will trigger the onSave props, that come directly from the app
So the app should care only about value and the state of the modal.
The modal component should care only about his value and open state and callback on ok
The higher component contain the input logic and pass pther props to modal component.
Maybe this is not the best way, but is the best i could develop with my limited knowledge