React js - Ant Design switch changing although Ok button is not pressed - reactjs

Hello I am having a problem here in my switch and by the way I am using Ant Design switch. What I want to happen is when I click the switch a popup should appear first asking if the user wants to switch on or off and when the user click ok the switch should change.
Here is my code
import Modal from 'antd/es/modal';
import Switch from 'antd/es/switch
const { confirm } = Modal;
handleOk = () => {
confirm ({
title: Are you sure you want to switch on ?
okText: 'Yes',
cancelText: 'No',
onOk() {
console.log('The switch is now on')
}
}
And here is my code for switch
<Switch onClick={this.handleOk} />
The output is when I click the switch the popup or modal appear and when I click the Ok button it logs in the console "The switch is now on". My only problem is the switch is moving although I didnt click the Ok button.

You need to make it as a controlled component to control weather it must be switched or not.
export function MyComponent = () => {
const [checked, setChecked] = useState(false)
const handleClick = () => {
if (!checked) {
confirm ({
title: Are you sure you want to switch on ?
okText: 'Yes',
cancelText: 'No',
onOk() {
setChecked(true)
}})
} else {
setChecked(false)
}
}
return (
<Switch onClick={handleClick} checked={checked}/>
)
}

Related

React confirm alert handle key press = enter

import { confirmAlert } from "react-confirm-alert"; // Import
import "react-confirm-alert/src/react-confirm-alert.css"; // Import css
export default function App() {
const handleButtonPress = (event) => {
if (event.key === "Enter") {
alert("Click Yes");
}
};
const handleClick = (e) => {
confirmAlert({
title: "Confirm to submit",
message: "Are you sure to do this.",
buttons: [
{
label: "Yes",
onClick: () => {
alert("Click Yes");
},
onKeyPress: () => {
handleButtonPress();
}
},
{
label: "No",
onClick: () => {
alert("Click No");
}
}
]
});
};
return (
<div className="App">
<button
onClick={() => {
handleClick();
}}
>
Click
</button>
</div>
);
}
I'm testing react-confirm-alert.
I'm trying to handle Button Yes by pressing Enter. Both function onClick() for yes and no are working good, but press enter is not working.
Can someone let me know if I did something wrong?
There's several issues. This react-confirm-alert library looks like a poor library, I'd pick a different one if you can find one that suits your needs.
You're calling handleButtonPress() with no arguments, and then you're trying to read from the event object, which you don't pass.
const handleButtonPress = (event) => {
if (event.key === "Enter") {
The event.key line should be throwing an error since event is undefined. Since you're not seeing errors, it's clear this line isn't getting called. You should be using console.log or the debugger to double check what code is getting called.
You should also get in the habit of reading documentation. In this case, the documentation shows that onKeyPress is a top level setting, while you've incorrectly put it in buttons.
Either way, react-confirm-alert doesn't pass the event to the onKeyPress callback: https://github.com/GA-MO/react-confirm-alert/#options so it doesn't seem like this API should exist. It doesn't have any use.
I would either pick a different library, otherwise you'll need to add your own keypress listener to the document, and manually handle the enter key there.

clickOutside hook triggers on inside select

I have a card component which consists of 2 selects and a button, select1 is always shown and select2 is invisible until you press the button changing the state. I also have an onClickOutside hook that reverts the state and hides select2 when you click outside the card.
The problem Im having is that in the case when select2 is visible, if you use any select and click on an option it registers as a click outside the card and hides select2, how can I fix this?
Heres the relevant code from my card component:
const divRef = useRef() as React.MutableRefObject<HTMLInputElement>;
const [disableSelect2, setDisableSelect2] = useState(true);
const handleActionButtonClick = () => {
setDisableSelect2(!disableSelect2)
}
useOutsideClick(divRef, () => {
if (!disableSelect2) {
setDisableSelect2(!disableSelect2);
}
});
return (
<div ref={divRef}>
<Card>
<Select1>[options]</Select1>
!disableSelect2 ?
<Select2>[options]</Select2>
: null
<div
className="d-c_r_action-button"
onClick={handleActionButtonClick}
>
</Card>
</div>
);
};
And this is my useoutsideClick hook
const useOutsideClick = (ref:React.MutableRefObject<HTMLInputElement>, callback:any) => {
const handleClick = (e:any) => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
useEffect(() => {
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
});
};
Extra informtaion: Im using customized antd components and cant use MaterialUI
I tried to recreate your case from the code you shared. But the version I 'built' works.
Perhaps you can make it fail by adding in other special features from your case and then raise the issue again, or perhaps you could use the working code from there to fix yours?
See the draft of your problem I made at https://codesandbox.io/s/serverless-dust-njw0f?file=/src/Component.tsx

Smart PayPal Buttons in React, disable the button

I have this implementation of the PayPal smart buttons in React:
function PayPalButtonComponent(props: PayPalButtonProps) {
const [show, set_show] = useState(false);
const [error, set_error] = useState<string>();
const create_order = (_: any, actions: any) => {
return actions.order.create({
purchase_units: [
{
amount: {
currency: props.currency || "EUR",
value: props.total
}
}
]
});
};
const handle_approve = (_: any, actions: any) => {
return actions.order.capture().then((details: any) => {
if (props.onSuccess) props.onSuccess(details);
});
};
const handle_cancel = () => {
if (props.onCancel) props.onCancel();
};
const handle_error = () => {
if (props.onError) props.onError();
};
const render_button = () => {
const Button = paypal.Buttons.driver("react", { React, ReactDOM });
return (
<Button
style={{
layout: "horizontal",
size: "responsive",
shape: "rect",
color: "gold",
tagline: false
}}
funding={{
allowed: [paypal.FUNDING.CARD, paypal.FUNDING.PAYPAL],
disallowed: [paypal.FUNDING.CREDIT]
}}
createOrder={create_order}
onApprove={handle_approve}
onError={handle_error}
onCancel={handle_cancel}
/>
);
};
useEffect(() => {
if (props.isScriptLoaded) {
if (props.isScriptLoadSucceed) set_show(true);
else set_error("Unable to load the paypalscript");
}
}, [props.isScriptLoaded, props.isScriptLoadSucceed]);
if (error) return <p>{error}</p>;
if (!show) return <FakeButton />;
return render_button();
}
I have struggled to implement these buttons in react since there is no documentation, I have found and copied some code from here and trying to guess other stuff. But I can't understand how to disable the button.
In this guide they state that one can call the disable() method on the actions object but can't figure out how I can accomplish that with my configuration.
Have you ever try something similar? Do you know any documentation one can follow?
edit
What I'm trying to accomplish is to set the button in a disable state during the payment. I know there is the paypal overlay, but when the transaction completes I change the app route and since it happen when onSuccess is called, due to the apparent async nature of actions.order.capture() this can't happen instantaneously, and so there is a moment when one can click again on the paypal button. If i can disable the button i have solve the problem.
The onInit implementation allows you to disable/enable the button before clicking on it, useful for some sort of validation before the checkout (like terms checked) but doesn't apply to my case. I have also tried to call actions.disable() in create_order but that breaks the button.
Have same situation here and the onInit example from documentation is not that straight forward as some think... You would need to create hidden imput and assign somsort of variable to it when payment made in order to achieve buttons to be disabled after payment. To bad no one solve this one.

Detecting user leaves page with custom popup in reactjs

I want to open a popup to warn the user that their data would be lost if they leave the form without submitting it.
I've heard about Prompt can do it, but they didn't support I customize the Popup, button, or handling confirmation button or cancel.
is there any way or component that can help me to show the popup prevent the user leaves the form with 2 customize buttons.
Thank you in advance.
use react-confirm-alert
install:
npm i react-confirm-alert
using:
import { confirmAlert } from 'react-confirm-alert';
import 'styles/react-confirm-alert.css';
create this customize function
const MyCustomConfirm = (content, onAccept, title, acceptTxt, rejectTxt) => {
acceptTxt = acceptTxt || "Yes";
rejectTxt = rejectTxt || "No";
title = title || "Message";
confirmAlert({
title: title,
message: <div className="react-confirm-alert-message">{content}</div>,
buttons: [
{
label: acceptTxt,
className: "btn-secondary", // your own css
onClick: () => {
if (onAccept) {
onAccept();
}
}
},
{
label: rejectTxt,
className: "btn-secondary", // your own css
onClick: () => { }
}
]
});
}
now you can call it anywhere. example :
MyCustomConfirm(' Do you want to clear all ? ', function () {
searchInput.val("");
$(this).val("");
});
use onBlur event on your parent element of form
myFunction = () =>{
MyCustomConfirm(' Do you want to clear all ? ', function () {
searchInput.val("");
$(this).val("");
});
return false;
}
render(){
// your parent element of form
<form onBlur={this.myFunction()>
</form>
}
or you can call this popup when user try to close your form.
or even the document level you can use this :
window.addEventListener("beforeunload", function (e) {
if (formSubmitting) {
return undefined;
}

Status of React Navigation drawer? (open or closed)

I'm building a drawer with React Navigation and want to perform some logic if the user closes the drawer. I don't see anything obvious in the documentation that will allow me to do this. Is anyone aware of a way to do this?
You need to custom navigation actions to capture the DrawerClose event:
const MyDrawerNavigator = DrawerNavigator({
//...
});
const defaultGetStateForAction = MyDrawerNavigator.router.getStateForAction;
MyDrawerNavigator.router.getStateForAction = (action, state) => {
//use 'DrawerOpen' to capture drawer open event
if (state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerClose') {
console.log('DrawerClose');
//write the code you want to deal with 'DrawerClose' event
}
return defaultGetStateForAction(action, state);
};
According to #ufxmeng
import {
StatusBar,
} from "react-native";
const MyDrawerNavigator = DrawerNavigator({
//...
});
const defaultGetStateForAction = MyDrawerNavigator.router.getStateForAction;
MyDrawerNavigator.router.getStateForAction = (action, state) => {
if(state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerClose') {
StatusBar.setHidden(false);
}
if(state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerOpen') {
StatusBar.setHidden(true);
}
return defaultGetStateForAction(action, state);
};
See here https://github.com/react-community/react-navigation/blob/673b9d2877d7e84fbfbe2928305ead7e51b04835/docs/api/routers/Routers.md
and here https://github.com/aksonov/react-native-router-flux/issues/699
This is for anyone using the v2.0+ version of react-navigation. There are now drawer actions you can track.
{
OPEN_DRAWER: 'Navigation/OPEN_DRAWER',
CLOSE_DRAWER: 'Navigation/CLOSE_DRAWER',
TOGGLE_DRAWER: 'Navigation/TOGGLE_DRAWER',
DRAWER_OPENED: 'Navigation/DRAWER_OPENED',
DRAWER_CLOSED: 'Navigation/DRAWER_CLOSED'
}
react-navigation.js line 825
However, it seems that implied drawer navigations from say swiping don't fire the OPEN_/CLOSE_ actions, since you didn't manually toggle it. The _OPENED/_CLOSED actions do fire afterwards, though.
const MyDrawerNavigator = createDrawerNavigator(...);
const defaultGetStateForAction = MyDrawerNavigator.router.getStateForAction;
MyDrawerNavigator.router.getStateForAction = (action, state) => {
switch (action.type) {
case 'Navigation/OPEN_DRAWER':
case 'Navigation/DRAWER_OPENED':
StatusBar.setHidden(true, 'slide');
break;
case 'Navigation/CLOSE_DRAWER':
case 'Navigation/DRAWER_CLOSED':
StatusBar.setHidden(false, 'slide');
break;
}
return defaultGetStateForAction(action, state);
};
Working with "react-navigation": "^3.5.1"
const defaultGetStateForAction = DrawerNav.router.getStateForAction;
DrawerNav.router.getStateForAction = (action, state) => {
if(action){
if(action.type == 'Navigation/MARK_DRAWER_SETTLING' && action.willShow){
StatusBar.setHidden(true);
} else if(action.type == 'Navigation/MARK_DRAWER_SETTLING' && !action.willShow) {
StatusBar.setHidden(false);
}
}
return defaultGetStateForAction(action, state);
};
For anyone looking to wire it up such that the drawer events are available in one of your Screens or Components instead of the top level app, I was able to wire that up by using screenProps as described in this post. You first set up the screenProps on your app and pass in the router and whatever else you need. You can pull screenProps off the props and use it your screen or component (I wired it up in the constructor in this example), use getStateForAction to setState in your component driven off the router events.
Here is an example (some code removed for clarity)
App.js
import React from 'react';
import { AppLoading } from 'expo';
import {
createDrawerNavigator,
createAppContainer,
createStackNavigator,
} from 'react-navigation';
import { HomeScreen } from './src/screens/HomeScreen.android';
import { LanguageSelectScreen } from './src/screens/LanguageSelectScreen';
export default class App extends React.Component {
state = {
isLoadingComplete: false,
isTilted: false,
};
constructor() {
super();
}
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading />
);
} else {
return (
<MyApp screenProps={MyAppNavigator.router} />
);
}
}
}
const MyAppNavigator = createDrawerNavigator(
{
Home: {
screen: HomeScreen,
},
PlayerNameScreen: {
screen: PlayerNameScreen,
},
},
{
unmountInactiveRoutes: true,
initialRouteName: 'PlayerNameScreen',
},
);
const RootStack = createStackNavigator(
{
Main: {
screen: MyAppNavigator,
},
MyModal: {
screen: LanguageSelectScreen,
},
},
{
mode: 'modal',
headerMode: 'none',
},
);
export const MyApp = createAppContainer(RootStack);
HomeScreen.android.js
import React from 'react';
import {Icon} from 'react-native-elements';
export class HomeScreen extends React.Component {
static navigationOptions = {
drawerLabel: () => 'Home',
drawerIcon: ({ tintColor }) => (
<Icon
name="checkerboard"
type="material-community"
size={25}
color={tintColor}
/>
),
};
constructor(props) {
super(props);
const router = props.screenProps;
const defaultGetStateForAction = router.getStateForAction;
router.getStateForAction = (action, state) => {
switch (action.type) {
case 'Navigation/MARK_DRAWER_SETTLING':
if (action.willShow == false) {
console.log('CLOSED');
this.setState({ isTilted: false });
} else if (action.willShow == true) {
this.setState({ isTilted: true });
console.log('OPEN');
}
break;
}
return defaultGetStateForAction(action, state);
};
this.state = {
isTilted: false,
};
}
render() {
const { isTilted } = this.state;
// ... render using isTilted
}
}
Now in react navigation 5 you can directly access the status of your drawer using this approach :
useIsDrawerOpen() is a Hook to detect if the drawer is open in a parent navigator.
For exemple in your view you can test if your drawer is open or not directly using this approach :
import { useIsDrawerOpen } from '#react-navigation/drawer';
const MainContainer = () => {
return (
<View style={(useIsDrawerOpen()) ? styles.conatinerOpenStyle : styles.containerClosedStyle}>
<Text>{console.log("Test drawer "+useIsDrawerOpen())}</Text>
</View>
);}
I found something similar to asdfghjklm.
Here's my code which is working perfectly, even when you use a swipe to open - and then use a button to close it:
openCloseDrawer = (props) => {
if (props.navigation.state.index == 1) {
props.navigation.navigate('DrawerClose');
} else {
props.navigation.navigate('DrawerOpen');
}
}
I execute this function when the user taps on the "Open/Close Drawer" button.
Based on Brad Bumbalough, fredrivett (thank you mans) solution I find a more fast response solution (the other solution delays secons in some uses).
const defaultGetStateForAction = StackLogadoDrawer.router.getStateForAction;
StackLogadoDrawer.router.getStateForAction = (action, state) => {
switch (action.type) {
case 'Navigation/MARK_DRAWER_SETTLING':
if (action.willShow == false) {
console.log('CERRADO');
} else if (action.willShow == true) {
console.log('ABIERTO');
}
break;
}
return defaultGetStateForAction(action, state);
};
This is fired just immediately actions occurs (or very near).
It works on gestures or calling openDrawer()
Anyway I think this is a "must have" easy and direct way in the API
Hmm not really seeing much. One way would be to control the opening/closing of the drawer manually using:
this.props.navigation.navigate('DrawerOpen'); // open drawer
this.props.navigation.navigate('DrawerClose'); // close drawer
That way you can wrap your close/open events in a function where you can do whatever you want before opening/closing.
The only other possible solution I saw in their docs was using a custom contentComponent. You can pass a function to the onItemPress(route) event, so maybe you can try hooking into that.
This is working fine in React-Native, not sure about React.
For my case I had to change the StatusBar so I did not need to know wether the Drawer is closed fully or not, So below code worked for me...
props.navigation.state.drawerMovementDirection === 'closing'
? //it is closing not fully closed
: (props.navigation.state.drawerMovementDirection === 'opening' ||
props.navigation.state.isDrawerOpen) && (
//fully opened or opening
)
If you don't need to know immediate then you can use below code, and this will work fine and will give you accurate answer whether the Drawer is Open or Not!
Note: This will be delayed answer, in my case it was taking 1 sec.
props.navigation.state.isDrawerOpen?
//open
:
//close;
If the solution does't work I am very sorry, but above all answer did not worked! So this is the version which is working for me:))
useEffect(() => {
const _unsubscribe = props.navigation.addListener('state', (data) => {
var array = data.data.state.routes[0].state.history;
const mydata = array.some(item => item.hasOwnProperty('status'))
console.log('data--->', mydata)
if (mydata) {
console.log('daawer close ')
} else {
console.log('daawer open ')
}
});
return _unsubscribe;
}, [])
Without Redux integration can be used onNavigationStateChange on router component. Just intercept drawer actions: DrawerOpen and DrawerClose.
Example:
handleNavigationState = (previous, next, action) => {
if (action.routeName === 'DrawerOpen') {
this.props.setDrawerState(true);
} else if (action.routeName === 'DrawerClose') {
this.props.setDrawerState(false);
}
}
render() {
return (
<Router onNavigationStateChange={this.handleNavigationState} />
);
}
I know this is late, but for anyone who is looking for an answer:
The logic for the drawer being open/closed is in:
this.props.navigation.state.routes[0].index
It's 0 for closed, 1 for open.
You can also toggle the Drawers with
this.props.navigation.navigate('DrawerToggle')
instead of
this.props.navigation.navigate('DrawerOpen');
or
this.props.navigation.navigate('DrawerClose');
It seems to be more convenient for me and I have not happened upon any problems yet. Although it's nice to know whether they are toggled or not in order to invoke other actions.
I truly believe React-Navigation has one of the worst documentation that I have ever seen. There are commands that nobody knows about. I could not find the DrawerToggle action in the documents, and I only happened upon it through using console.log(this.props.navigation);

Resources