React-Redux : how to make ReCaptcha a required field - javascript

in my react-redux form, i want to make recaptcha a required field, and disable my navigation bar component until recaptcha response is verified, i found some similar questions with javaScript but i wasn't able to apply them with React, because i am using react-recaptcha plugin :
<div className="form_wrapper">
<ReCAPTCHA
sitekey="xxxxxxxxxxx"
render="explicit"
onloadCallback={this.callback}
verifyCallback={this.verifyCallback}
/>
</div>
<NavigationBar
fields={requiredFields}
// disableNext={this.props} (here where i make conditions to disable)
/>
here are my callback and verifyCallback methods:
verifyCallback(response) {
return response;
}
callback() {
console.log('Done !!');
}
thank you
i added the code suggested by Hardik Modha, as follows but still having the same issue:
<NavigationBar
fields={requiredFields}
disableNext={this.props ... && !this.validateForm()}
/>
verifyCallback(response) {
this.setState({
reCaptchaResponse: response,
});
}
validateForm() {
if (!this.state.reCaptchaResponse || this.state.reCaptchaResponse.trim().length === 0) {
return false;
}
return true;
}

var Recaptcha = require('react-recaptcha');
// specifying verify callback function
var verifyCallback = function (response) {
this.setState({
reCaptchaResponse: response
});
};
ReactDOM.render(
<Recaptcha
sitekey="xxxxxxxxxxxxxxxxxxxx"
render="explicit"
verifyCallback={verifyCallback}
onloadCallback={callback}
/>,
document.getElementById('example')
);
You can pass a prop verifyCallBack to react-recaptcha, in that callback function you can store the response in state or wherever you want. Now, if response is empty you can simply disable the next button or you can put validation when user clicks on validation.
e.g. If you are storing response in state then you can check whether reCaptcha response is empty or not.
validateForm() {
// other field validations....
if (!this.state.reCaptchaResponse || this.state.reCaptchaResponse.trim().length === 0) {
return {success: false, message: 'Captcha is required.'};
}
}
Edit: For the edited question,
You can also create a state variable say btnDisabled and initialize it with true.
constructor() {
super();
this.state = {btnDisabled: true};
}
and Next button as
<button disabled={this.state.btnDisabled}>Next</button>
Now, in your validateForm method you can check whether the reCaptcha response is empty or not and based on that you can set the btnDisabled variable to true or false.
validateForm() {
// other field validations....
if (!this.state.reCaptchaResponse || this.state.reCaptchaResponse.trim().length === 0) {
return {success: false, message: 'Captcha is required.'};
} else {
this.setState({
btnDisabled: false
});
}
}
Side Note: You should never rely on client side validations. User can easily bypass client side validations. So, You should implement server side validations, too.

Related

How can I disable validation on submit?

I have a multistep form, and I keep each step under a single <Formik /> tag. The initial values and validation schemas are kept in objects for each step:
const initialValues = {
step1Values: { ...values1 },
step2Values: { ...values2 },
step3Values: { ...values3 },
}
const validationSchema = Yup.object().shape({
step1Schemas: { ...yupSchema1 },
step2Schemas: { ...yupSchema2 },
step3Schemas: { ...yupSchema3 },
});
// in component render/return:
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
validateOnMount
validateOnBlur
/>
Each step has its own component rendered within the Formik tag, and use useFormikContext to grab values, errors, i.e. with const formikProps = useFormikContext(), then for a given value of a field, I can say:
// in step 1
<input
name="step1Values.someValue"
value={step1Values.someValue}
onChange={formikProps.handleChange}
/>
I want all components under the same form umbrella for persistence purposes (form values will persist even as they change from one step to another). However, there are certain cases where the user can skip step one or two and not fill them out, and jump to step 3 (based on state of the redux-store). Submitting the form is only dependent on step 3 being properly validated according to formik.
I have internal checks in components 1 and 2 to move through those steps, but I want formik to track the errors to show errors if the fields are empty after having been touched. In cases where users can skip step 1 or 2, the validation schema has errors because those fields are considered required. I have a custom validation function customValidate which returns true or false, and which is based on the current state of formikProps.errors.step3. The submit button is disabled until customValidate returns true, so I really don't need formik to validate on submit - in fact, I need it to not validate on submit, so that the onSubmit function fires properly from step 3, regardless of errors in step 1 or 2.
How can I keep validateOnMount and validateOnBlur, but prevent validate on submit? I wish there was a validateOnSubmit={false} option, but I don't see that. There must be a way?
I have a workaround for you: Just remove the validation Schema, validateOnMount, validateOnBlur because we are going to handle them ourselves.
make your Formik as Follows
<Formik
initialValues={initialValues}
/>
you will need this function to format the errors for you:
const getErrorMessages = ({ path, message, inner }) => {
if (inner && inner.length) {
return inner.reduce((acc, { path, message }) => {
acc[path] = message;
return acc;
}, {});
}
return { [path]: message };
};
and then you can validate your form onMount using useEffect like follows
useEffect(() => {
try {
validationSchema.validateSync(values, { abortEarly: false });
setErrors({});
} catch (error) {
setErrors({ ...getErrorMessages(error) });
}
}, []);
and for the onBlur make your own Function
const handleBlur = async (e) => {
setFieldTouched(e.target.name, true);
try {
validationSchema.validateSync(values, { abortEarly: false });
setErrors({})
} catch (error) {
setErrors({ ...getErrorMessages(error) });
}
};
finaly to your inputs you can make them as follows:
<input
name="step1Values.someValue"
value={step1Values.someValue}
onChange={formikProps.handleChange}
onBlur={handleBlur}
/>
final Notes:
you have to use your custom onBlur on all fields or it won't validate onBlur
you can make the same thing with onChange if you want just make a custom function like handleBlur one.

How can i use localStorage to maintain state after a page refresh in React

Each time a user logs in, i want the state to remain at 'true' even if there is a page reload.
The state is set initially to false, (let _authed = false).
But when i reload the page, it goes back to false, which is the index page.
What i did
When the user logs in, i save the user's details in localStorage and when the user logs out, i cleared the localStorage and i set it to false. (this works fine)
In the setAuthed() function, i tried to check if the user i stored in localStorage is not null, it should keep the authed variable to true.
But its not working when i refresh the page. Is there anything, i am doing wrong? Help appreciated.
let _authed = false;
// User logs in, set user in localStorage
saveUser(user){
_user = user;
localStorage.setItem('user', JSON.stringify(user))
},
//User clicks logout, set state to false
setLogout(){
_authed = false;
localStorage.clear()
},
// If there is a user in local storage, always set state to true
setAuthed(){
if (localStorage.getItem("user") !== null) {
_authed = true;
}
},
getAuthed(){
return _authed;
},
You can use React lifecycle methods to read and write some kind of persistent state. Here I've wrote a tiny example to show it.
class Root extends React.Component {
state = {
isLoggedIn: false
}
componentDidMount () {
const persistState = localStorage.getItem('rootState');
if (persistState) {
try {
this.setState(JSON.parse(persistState));
} catch (e) {
// is not json
}
}
}
componentWillUnmount () {
localStorage.setItem('rootState', JSON.stringify(this.state);
}
render () {
return (
<div>
{this.props.children}
</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Disable and re-enable button on single action

I need to disable and re-enable a button during the async call. I am only able to disable it. If I add code to re-enable it is ignored. I acknowledge I may not be asking the right question.
I have a function with a button "Action":
<button className={`doFoo${buttonClasses[type]} ${type}`} onClick={onClick} disabled={isButtonDisabled}>
That is called by a React class "Actions":
<Action type="like" onClick={onLike} isButtonDisabled={isButtonDisabled} />
That is called by another React class "List":
<Actions onLike={this.handleLike} onDislike={this.handleDislike} isButtonDisabled={isButtonDisabled}/>
Also in that class is are the following functions:
...
thumbsUp() {
const {
...
} = this.props;
const {
...
} = this.state;
this.setState({ wasLiked: true, viewProfile: false }, () => {
setTimeout(doThumbsUp, ACTION_CONFIRMATION_ANIMATION_TIMEOUT);
});
function doThumbsUp() {
thumbsUp({
index: activeIndex,
id: profiles[activeIndex].id
});
}
},
handleLike() {
const { showThumbsUpConfirmation } = this.props;
if (showThumbsUpConfirmation) {
this.showThumbsUpConfirmationModal();
} else {
this.thumbsUp();
}
},
...
Here's what the source looks like:
export function thumbsUp({ id, ... }) {
return api.post(`${API.ENDPOINTS.FOO}/${id}/thumbs_up`, {
...
});
}
I can place this.setState(isButtonDisabled: true) at various places in this code and that value makes it to the view and disables the button. However I cannot figure out how to re-enable the button after the work has been done.
If I'm understanding you correctly you want the button to be disabled during async and after async be enabled? If that is the case, wherever you are calling the function that makes the api call, you just need to chain a .then(() => this.setState(isButtonDisabled: false) and that will update the state as soon as response has been received from api call. also if you aren't using es6 just make sure to set this to a variable above the api call to ensure its scoped properly for setState

how to create a asynchronous function to return user state after login using Meteor.loginWithPassword()

i try to create a function that return user state after login with Meteor.loginWithPassword() but it's asynchronous, the function always return undefined, how can i solve that?
var state;
Meteor.loginWithPassword(action.email, action.password,
(err) => {
if (err) {
alert('Đăng nhập thất bại')
} else {
state = "login success"
}
})
return state
Your state variable is not a reactive variable, so if value of state update it will not update the spacebar code inside html template.
You can solve this problem using any reactivating thing like Session, reactive var or reactive dict.
//Make sure you have install reactive var package
var state = new ReactiveVar('');
Template['name'].helpers({
'getState': function (userId) {
Meteor.loginWithPassword(action.email, action.password,(err) => {
if (err) {
alert('Đăng nhập thất bại')
} else {
state.set("login success");
}
})
console.log(len.get()); // You will get 2 when response come from you method call.
return state.get();
}
});
You can also use spacbar helper from Account UI named 'currentUser'. So your html will be like:
{{#if currentUser}}
<!-- Do something when user is login -->
{{else}}
<!-- Do something when user is not login -->
{{/if}}
Ask a callback argument/parameter to your function which you will call when login is successful
function login(action, callback) {
Meteor.loginWithPassword(action.email, action.password, err => {
if ( err ) {
//...
} else {
callback('login success');
}
});
}

Reactjs: Loading view based on response

Looking for a way for React to process some json, and load views based on the response.
For example:
1- React has a form, response goes out to external API
2- API processes the input, returns a success code unless there was validation issues, and send a response back to the React app
3- React gets the json response, loads a "Success" view, or reloads the form and outputs the erros
Is there a simple way for React to handle this? Thanks in advance!
Very simple...
Basically, you need to track when you initiate request (sending data) and when request is completed (receiving response).
Based on data returned, you decide what to render...
Take a look at this example (working fiddle)
// In this example, we are using JSONPlaceholer service do real
// request and receive response;
const root = 'http://jsonplaceholder.typicode.com';
const Success = () => (<div>Success!</div>);
const Error = () => (<div>Error! Fix your data!</div>);
const Form = React.createClass({
getInitialState() {
return {
processing: false,
result: undefined,
};
},
submit(event) {
this.setState({ processing: true });
event.preventDefault();
fetch(`${root}/posts`, {
method: 'POST',
data: {
// your data here...
}
})
.then(response => {
// We simulate succesful/failed response here.
// In real world you would do something like this..
// const result = response.ok ? 'success' : 'error';
const processing = false;
const result = Math.random() > 0.5 ? 'success' : 'error';
this.setState({ result, processing });
});
},
render() {
const { result, processing } = this.state;
if (result === 'success')
return <Success />;
return (
<form>
Form content here<br/>
<button onClick={this.submit}>
{ processing ? 'Sending data...' : 'Submit' }
</button>
{ result === 'error' && <Error /> }
</form>
);
},
});
render(<Form />, document.getElementById('root'));
The easy way would be to trigger the new state with setState() from the API callback function such as in the example below, although I recommend using a library such as Redux for state management.
var MainComp = React.createClass({
getInitialState: function() {
return {someProp: ""}
},
callAPI: function() {
var root = 'http://jsonplaceholder.typicode.com';
$.ajax({
url: root + '/posts/1',
method: 'GET'
}).then(function(data) {
this.setState({someProp: data.body})
}.bind(this));
},
render: function(){
return (
<div>
<h2>{this.state.someProp}</h2>
<button onClick={this.callAPI}>Async</button>
</div>
)
}
});
React.render(<MainComp/>, document.getElementById("app"));
Please note this is a naive example, you should still cover up error cases and build a logic to trigger different views based on state.

Categories

Resources