I'm struggling with getting a modal to appear onClick(). I have a function within a component that adds players to an existing list when clicking on Add Player. The button is rendered separately in a renderAddButton() function, which passes onAddButtonClick() as a prop.
I would like for the user to be able to input the player's name in a form within a modal before it is added to the list, right now the code outputs a Player + index as the name of the player.
function onAddButtonClick() {
setItems((prev) => {
const newItems = [...prev];
newItems.push({
name: `Player ${newItems.length + 1}`,
teamId: currentTeam[0].teamId
});
playersStore.push({
name: `Player ${newItems.length + 1}`,
teamId: currentTeam[0].teamId
});
return newItems;
});
}
I have this form which I want to represent in the modal:
export const PlayerForm = () => {
return (
<div>
<form>
<input type='string' id='playerId' name='playerName' defaultValue='0' />
<input
type='number'
id='playerGoals'
name='totalGoals'
defaultValue='0'
min='1'
max='5'
/>
<input
type='number'
id='playerGoals'
name='playerGoalPercentage'
defaultValue='0'
min='1'
max='5'
/>
</form>
</div>
);
};
How do I trigger the modal from inside onAddButtonClick()?
I implement modals using the react-bootstrap framework.
From the component that I want to display the modal from, I will create a handler that will govern the component's ability to show the modal based on the bool I set in state. Typically from the parent component this show handler would look like this:
setShow = () => {
this.setState({ show: !this.state.show });
};
As seen in the example this handles a state attribute called show which is what dictates whether or not the modal gets to display in app.
Below is the implementation of the modal I would use as a child component to the parent component where it would reside and where I would pass the state attribute which I called show that dictates with true or false whether or not to display the modal:
<ExampleModal
show={this.state.show}
setShow={this.setShow}
activeRecord={this.state.activeRecord}
activePrimaryAccountId={this.state.activePrimaryAccountId}
userAccessRole={this.props.userAccessRole}
/>
I pass the necessary details that the modal needs to display as props that I get from the the parent component's state attributes. The most important being the show attributes to include the setShow function which I use in the child component (the modal itself) to update state in the parent component to close the modal when the time comes also.
In the ExampleModal component I start off with declaring state with the following attributes already loaded from props:
this.state = {
show: this.props.show,
...
}
I then use a handler that takes advantage of the setShow function passed down to the child component in props as shown:
handleClose = () => this.props.setShow(false);
In the modal component there is a button that uses this handler in its onClick() synthetic event to trigger the closing of the modal after it has rendered to the browser.
Conversely in the parent component, your button will use the onClick() synthetic event to trigger a call that would be implemented something like this in the button to open the modal:
onClick={this.setShow(true)}
I reuse that process in all of my modals in React.js, hope that helps. The trick here is using componentDidUpdate() or useEffect() (if you're using React Hooks) effectively to make sure you have the right data loaded in state in the parent component so that you can pass it into the props of the child component at the right time. The <ExampleModal /> I gave you should give you enough of a clue.
Related
I have the following problem, in my web site build with nextJS and ReactJS with typescript I have products that are loaded when a button is clicked, when I click the button the items appeared and the button is scrolled down, which is the asked behavior, but when I scroll to the bottom of the page and I try to click the button the scroll remains on the same position and the items are loaded but cannot be seen, my logic is to use onFocus on the current button and when I click it to change the scroll to him, that will solve the problem when the user has scrolled down to the bottom of the page, that way it will not remain on the bottom but rather it will automatically scroll up to the button and will see the new items loaded.
The problem is that the logic to load the products are in a different component in which I am reusing the current button and right prop I am sending function to the onClick on the button. My question is how can I use onFocus. Does it has to be in the child component inside the function or in the button component. I tried to make it work on the Button component, but it doesn't work. So I am stuck for the last 4 hours and I really need a push. I would be glad if you could shine some enlargement
Here I will enter the function in the parent component for the onClick prop :
const handleLoadMoreProducts = () => {
if (!isSearchPage) {
const mappedBreadcrumbs: string[] = categoryData.breadcrumbs.map(
(crumb: BreadCrumItem) => crumb.name
);
gtmBreadcrumbTrack(mappedBreadcrumbs);
}
<LoadMoreProducts handleLoadMoreProducts={handleLoadMoreProducts} />
And here is the component that uses the Button:
interface LoadMoreProductsProps {
handleLoadMoreProducts?: (MouseEvent: React.MouseEvent<HTMLButtonElement>) => void;
Focus?: (MouseEvent: React.MouseEvent<HTMLButtonElement>) => void;
}
const LoadMoreProducts: FC<LoadMoreProductsProps> = ({ handleLoadMoreProducts }) => (
<div className="LoadMoreProducts">
<Button type="button" importance="ghost" onClick={handleLoadMoreProducts}>
Load more products
</Button>
</div>
);
I think what you want to do is to forward the ref of the element you are trying to focus in the Button component using React.forwardRef and combine it with the useImperativeHandle hook in order to gain the ability to trigger the focus with the ref outside of the Button component.
You could create a ref for the element you are trying to focus and call the focus() function for the ref on click.
More information regarding forwarding refs and the useImperativeHandle hook.
I have a parent component which contains a child, which in turn contains a md-dialog component. I open the dialog from the component with this:
ParentComponent.vue
<template>
<div>
<ConfirmDialog
:dialogState="isDialogShown"
#closeDialog="value => isDialogShown = false"
/>
<button #click="handleOpenModal()">open modal</button>
</div>
</template>
<script>
export default {
name: 'ParentComponent',
data() {
return { isDialogShown: false }
},
methods: {
handleOpenModal() {
this.isDialogShown = true;
}
}
}
</script>
Button click opens this dialog:
ConfirmDialog.vue
<template>
<md-dialog :md-active.sync="localDialogState">
markup
</md-dialog>
</template>
<script>
export default {
name: "ConfirmDialog",
props: ["dialogState"],
computed: {
localDialogState: {
get() {
return this.$props.dialogState;
},
set() {
this.handleCloseDialog();
}
}
},
methods: {
handleCloseDialog() {
this.$emit("closeDialog");
}
}
};
</script>
I don't really like this because it uses this localDialogState's setter to perform this side effect which only eventually funnels back down to set localDialogState after setting the prop on the parent, however so far I've had to use the setter so I can capture events like backdrop click or ESC keypress that are meant to close the modal.
NB, I have had to use this "local" version of dialog state because apparently props and computed props are all just kept on this object's instance, so the names collide otherwise. There might also be a better way to do this which I am open to hear suggestions on since the above are my first lines of Vue I have ever written.
What is the canonical way of updating these (.sync'd) props in order to catch close events triggered by md-mialog?
Obviously a dialog cannot be used by only one boolean prop. That way you can't distinguish when a dialog was cancelled and when it was confirmed. In more complex cases you may have several confirm buttons with different actions.
I recommend to use a method like open to open a dialog by using $refs in a parent component and close a dialog inside itself with emitting confirmation events that can be catched in a parent component.
I have a button component with the following props:
onClick: () => void - Triggers button click
Caption: string - Button text
And a Popup component with the following props:
onCancelPopup: () => void - Placed in the cancel button
onOkPopup: () => void - Fires if the user selects OK
IsPopupVisible: boolean - Determines popup visibility
PopupMessage: string - Like a confirmation message
I'm trying to create a wrapper component composed of Button and Popup. Using this wrapper I want to trigger the popup dialog. I plan to use this in a page with multiple buttons, each of which will trigger its own confirmation popup.
The easiest way to do this is wrap it in a BtnConfirm wrapper component as follows:
<>
<Button onClick={this.props.showPopup} Caption={this.props.Caption} />
<Popup IsPopupVisible={this.props.Visible} onCancelPopup={this.props.hidePopup}>
{this.props.PopupMessage}
</Popup>
</>
Say CallingComponent is using <BtnConfirm />. Then it will need to define Visible in its state and show/hide it in separate functions
as follows:
showPopup = () => setState({ Visible: true })
hidePopup = () => setState({ Visible: false })
popupOkClick = () => alert("OK Clicked");
And <BtnConfirm /> will have to receive all the props of both Button and Popup as props in the CallingComponent.
My question is, if I have mutiple instances of BtnConfirm in CallingComponent, wouldn't that mean I'll need to define Visible states for each of them? Is there a better way of achieving the same result without having to do this?
So I have a component which looks sth. like this:
class TestComponent extends Component {
constructor(props) {
this.state = {}
}
render() {
const {id, neededId, handleOnClick } = this.props
return (
<div id="reviews" onClick={handleOnClick}>Click me</div>
)
}
}
So the use case is that I click on an anchor on the same page (somewhere in a parent component) which will scroll to the <div /> and the onClick should then also be triggered, if the neededId === id.
So basically, the onClick should be triggered of course if I click the <div /> OR if I click on some anchor on the page and get scrolled to the <div /> element. The neededId gets mapped to the props via redux and is always available in the props as soon as a user clicks on the anchor.
Just imagine the <div /> as an accordion which should open if I click on the anchor which will scroll the user to the closed accordion.
How can I achieve this with React? Like, I know the super easy solution with jQuery or something, but that's not wanted obviously! :-D
Thanks!
I have a modal in React. When you click the background of the modal, the modal should close. The way I have it set up right now, if you click inside* the modal, it closes as well. Because the modal is inside the background>
handleClose(e) {
e.stopPropagation();
this.props.history.push('/business/dashboard')
}
render() {
return (
<Background onClick={e => this.handleClose(e)} name="BACKGROUND">
<Container onClick={console.log("IT CLICKED")} to={'/business/dashboard'} name="CONTAINER">
....
When I click on Container, the onClick event for Background gets called. I don't want this to happen. This is a form that users will be clicking on all the time. I need the modal to only close when you click outside the modal on Background.
I think it will work if you use stopPropagation on the Container click event instead of the Background. Just make sure that you use the onClick prop in your Container component.
class App extends React.Component {
handleClose = (e) => {
e.stopPropagation();
this.props.history.push("/business/dashboard");
};
render() {
return (
<Background onClick={this.handleClose} name="BACKGROUND">
<Container
onClick={e => e.stopPropagation()}
to={"/business/dashboard"}
name="CONTAINER"
/>
</Background>
);
}
}
EDIT: On rereading the question, the other answer is a simpler solution in this case.
The behavior you want to achieve is generally referred to as an "outside click" handler. There are a couple of decent libraries to handle this [0] and their source is pretty short and readable if you want to know how it works in detail. [1]
The general idea is to register a click event handler on the document in a HOC and check whether the event.target originates inside a React ref via Element.contains browser functionality. If is is, the handler will not be executed.
[0] https://github.com/tj/react-click-outside
[1] https://github.com/tj/react-click-outside/blob/master/index.js