I have react class component and I am trying to change it to function. But I don't know what to do with props.
This is my entire function.
const RegistrationForm = () => {
const [firstname, setFirstname] = useState('');
const [secondname, setSecondname] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmDirty, setconfirmDirty] = useState(false);
const handleSubmit = e => {
e.preventDefault();
axioswal
.post('/api/users', {
firstname,
secondname,
email,
password,
})
.then((data) => {
if (data.status === 'ok') {
dispatch({ type: 'fetch' });
redirectTo('/');
}
});
};
const handleConfirmBlur = e => {
const { value } = e.target;
setState({ confirmDirty: state.confirmDirty || !!value });
};
const compareToFirstPassword = (rule, value, callback) => {
const { form } = props;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
};
const validateToNextPassword = (rule, value, callback) => {
const { form } = props;
if (value && state.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
};
const { getFieldDecorator } = props.form;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
return (
<Form {...formItemLayout} onSubmit={handleSubmit}>
<Form.Item
label={
<span>
Firstname
<Tooltip title="What is your firstname?">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
>
{getFieldDecorator('Firstname', {
rules: [{ required: true, message: 'Please input your Firstname!', whitespace: true }],
})(<Input />)}
</Form.Item>
<Form.Item
label={
<span>
Secondname
<Tooltip title="What is your Secondname?">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
>
{getFieldDecorator('Secondname', {
rules: [{ required: true, message: 'Please input your Secondname!', whitespace: true }],
})(<Input />)}
</Form.Item>
<Form.Item label="E-mail">
{getFieldDecorator('email', {
rules: [
{
type: 'email',
message: 'The input is not valid E-mail!',
},
{
required: true,
message: 'Please input your E-mail!',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="Password" hasFeedback>
{getFieldDecorator('password', {
rules: [
{
required: true,
message: 'Please input your password!',
},
{
validator: validateToNextPassword,
},
],
})(<Input.Password />)}
</Form.Item>
<Form.Item label="Confirm Password" hasFeedback>
{getFieldDecorator('confirm', {
rules: [
{
required: true,
message: 'Please confirm your password!',
},
{
validator: compareToFirstPassword,
},
],
})(<Input.Password onBlur={handleConfirmBlur} />)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<Checkbox>
I have read the agreement
</Checkbox>,
)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">
Register
</Button>
</Form.Item>
</Form>
);
}
const WrappedRegistrationForm = Form.create({ name: 'register' })(RegistrationForm);
WrappedRegistrationForm.getInitialProps = async () => ({
namespacesRequired: ['common'],
})
export default withTranslation('common')(WrappedRegistrationForm);
For example in this line of code.
const { getFieldDecorator } = props.form;
I get this error.
ReferenceError: props is not defined
When I remove props then I get form is not defined. I know basic of props but cant figure out what to do in this case. I will be thankful for any help.
You're declaring a function component which receives its props as the first argument in the function.
const RegistrationForm = (props) => {
const [firstname, setFirstname] = useState('');
const [secondname, setSecondname] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmDirty, setconfirmDirty] = useState(false);
All react functional components take props as a first argument
In other words, what you want is
const RegistrationForm = (props) => {
You have to pass props like this const RegistrationForm = (props) => {
I recommend this reading 5 Ways to Convert React Class Components to Functional Components w/ React Hooks
Related
Hello World!
I want to render data into my contact form screen (I want the screen to be 100% configurable using call api)
Fisrt I want to render the title[Contact Us] and the subtitle[Enter your details to contact us] from the configuration Array -->Done
Second I want to render the objects on the Form Array into an inputs -->Not Yet
this is the Code:
import React, { useState,useEffect } from 'react';
import {
View,
Text,
TextInput,
SafeAreaView,
Keyboard,
ScrollView,
Alert,
} from 'react-native';
import COLORS from '../src/conts/colors';
import Button from '../src/views/components/Button';
import Input from '../src/views/components/Input';
import Loader from '../src/views/components/Loader';
const ContactForm = ({navigation}) => {
const [inputs, setInputs] = React.useState({
firstname: '',
lastname: '',
email: '',
note: '',
});
const [errors, setErrors] = React.useState({});
const [loading, setLoading] = React.useState(false);
const validate = () => {
Keyboard.dismiss();
let isValid = true;
if (!inputs.firstname) {
handleError('Please input first name', 'firstname');
isValid = false;
}
if (!inputs.lastname) {
handleError('Please input last name', 'lastname');
isValid = false;
}
if (!inputs.email) {
handleError('Please input email', 'email');
isValid = false;
} else if (!inputs.email.match(/\S+#\S+\.\S+/)) {
handleError('Please input a valid email', 'email');
isValid = false;
}
if (!inputs.note) {
handleError('Please input note', 'note');
isValid = false;
}
if (isValid) {
submitData();
}
};
const submitData = ()=>{
fetch("https://flow.simpas.ai:2021/react/contact",{
method:"post",
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify({
firstname: inputs.firstname,
lastname: inputs.lastname,
email: inputs.email,
note: inputs.note,
})
})
.then(res=>res.json())
.then(data=>{
alert(`${data.firstname} is saved successfuly`);
navigation.navigate("Home")
})
.catch(err=>{
alert("someting went wrong")
})
};
const handleOnchange = (text, input) => {
setInputs(prevState => ({...prevState, [input]: text}));
};
const handleError = (error, input) => {
setErrors(prevState => ({...prevState, [input]: error}));
};
const [data, getData] = useState([])
const URL = "https://flow.simpas.ai:2021/react/contact";
useEffect(() => {
fetchData()
}, [])
const fetchData = () => {
fetch(URL)
.then((res) =>
res.json())
.then((response) => {
console.log(response);
getData(response);
})
}
return (
<SafeAreaView style={{ flex: 1}}>
<Loader visible={loading} />
<ScrollView
contentContainerStyle={{paddingTop: 50, paddingHorizontal: 20}}>
<Text style={{color: COLORS.black, fontSize: 40, fontWeight: 'bold',fontFamily: 'Roboto',textAlign: 'center'}}>
{data?.title}
</Text>
<Text style={{color: COLORS.grey, fontSize: 18, marginVertical: 10,fontFamily: 'Roboto',textAlign: 'center'}}>
{data?.subtitle}
</Text>
<View style={{marginVertical: 20}}>
<Input
onChangeText={text => handleOnchange(text, 'firstname')}
onFocus={() => handleError(null, 'firstname')}
iconName="account-outline"
label="First Name"
placeholder="Enter your first name"
/>
<Button title="Contact Us" onPress={validate} />
</View>
</ScrollView>
</SafeAreaView>
);
};
export default ContactForm;
This is The Json Data:
{
"title":"Contact us",
"subtitle":"Enter your details to contact us",
"action":"form",
"configuration":[
{
"title":"Contact us",
"subtitle":"Enter your details to contact us",
"type":"action",
"actiontype":"form",
"posturl":"https://flow.simpas.ai/interview/content/123456/businesscard/",
"form":[
{
"fieldtype":"field",
"title":"First Name",
"fieldname":"firstname",
"placeholder":"Enter your first name",
"iconurl":"https://www.creativefabrica.com/wp-content/uploads/2019/02/People-Icon-by-Kanggraphic-1-580x386.jpg",
"fieldvalue":"",
"required":true
},
{
"fieldtype":"field",
"title":"Last Name",
"fieldname":"latstname",
"placeholder":"Enter your last name",
"iconurl":"https://www.creativefabrica.com/wp-content/uploads/2019/02/People-Icon-by-Kanggraphic-1-580x386.jpg",
"fieldvalue":"",
"required":true
},
{
"fieldtype":"email",
"title":"Email",
"fieldname":"email",
"placeholder":"Enter your email",
"iconurl":"data:image/png;base64,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",
"fieldvalue":"",
"required":true
},
{
"fieldtype":"textarea",
"title":"Note",
"fieldname":"note",
"placeholder":"Enter your note",
"iconurl":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAclBMVEX///8AAAAODg6UlJQwMDA6Ojrw8PClpaX7+/vCwsIYGBiGhoZTU1P29vbU1NTn5+fc3Nytra1+fn4rKytdXV1OTk7IyMhlZWWfn58dHR2Pj49wcHCoqKjOzs4ICAhYWFi1tbUkJCR3d3dBQUE3NzdHR0dAC+4LAAAFpElEQVR4nO2d65aiOhBGRQXxiiKKl9bBsef9X/Esp0+ThCPBxKoKfda3f3Yvk+xlSEUIVYMBAAAAAAAAAAAAAAAAAADAz2a1yXfxWIp4l29WknqXcxSC815GL63GQfwejDcpv+D+M5jfg0/27zHM/NQ5s/pl4SaoYpvxCa5noe3+MlxzCWah1WqYvsUktJdGwmK4MDvZFvlpJMMpL7Zm5wsOwZHRRb5acnTSynKVG/2P6LswLsIzzyyxk5T6EOgvRX2OTslbf40p5zxda42zLdZBR6HNENFNfoOVGkZJ27IWKT5oW3bkQw2Edi3Y1O3uSNt1Z1ePZEParpqkF9J23bnwTNNlHXDHlM16UW/+t5QRWQXDA2GrfhxYQqKaGiEX0i/Uckp5waiFhrBRX1iWmkMvDSmvmHrXfSVs1Jfr92Aod9+14ZCwUV+GMPQBhqLA0AsYigJDL2AoCgy9gKEoMPQChqLA0AsYigJDL2AoSmDDZfIunU8jghpO4uPsXY7xxN5JSMNfEQ2/+mo4IRKMIuu3GNAwJjOM+2m4PJIZHm3LTcDvcPt8uB5sbd0ENPxNZvi7p4Z0hxetB0lCRouMZq2J7Q/ow+5pVtP36ToqgH2pFzAUBYZewFAUGHoBQ1Fg6AUMRYGhFzAUBYZewFCRvk9XF0ENp+U9fpd72fHyX0hD8zVPf/K+GlZEglFU9dTwTmZ476fh//+5RXojM7zZFtSAs7R8PlwPrK8WBjSkyyphva0fMlpchs8H7MjQ/tpd2D3NdPI+XW/7Y1/qBQxFgaEXMBQFhl7AUBQYegFDUWDoBQxFgaEXMBQlqOF6XizcyN0zFIY0VKmIHHDOCxzQcO8jGEUnx8EENPzjZxg55uj+gc8tHDOk/cDnFo6G9efkZ+ni6fi7cRxM/TmWbGZWw/XT8XfS8TJek8z3g1ZejBZeD4HnjmNRnVDm3Hs14ieHuSMj52S5KrM/ZRGBHu3atFePKDMJ98hQSytOmd2zP4baU9ghZSLh/hiqNMJRTFmvpDeG+lGBgrLhvhgaFVJcw4yVfhiuza19Rdl2HwyTeWRCmtI4pGG6XC6T9eQ/2QxmpIWRQhlm+3l5vz0/w0KbHD6MYWX9sUKbWTyAYXroOH5EW/1B3nDaWXiJtj9pw7S7NJj9HLEzwobJrVOQukaDrOEr9wqOxEX0RA3bBQtVt490yzaQNUxabkqWhyxRyyt1HR1Jw50pdswnm2p6+XvwW+3byGvBCBoapcGGJ/13vPbrl7zilJyhcZJ6bh7ZVyHkRt6vnKEeCBtflFbribZM0AMxQ20dvTYXE5WkaUbfsZih9uZUM6Sf1L8q+o7FDJVF86eDNket7wx5ImWoiuN9Nv6TaonEOArmShmqgNcMB4USJC4p94WU4e27n3Fj26mfEGCpLilkuKwtGttOvbQjfaR4IGSoVpN9y9+78mJ6I2SoDnMYsVD/sUH6sEJDyLCejFd9O2r8muIqgSpUs6s2nGnf1Fq/I+V69uZlhOqu1QXsbmrLbRxDYroIB2K18+pwqOpIbnTBO1+VZaH6h/Wd+z/ffzHe8J8xlo2XqWGpDlX9e5ovM99/ZxQUqkOq4vpXVG8cXuEttCxSS3ZhyGSFKchcH1SiHrC2ag6apdz5S2VL1HRWcW80qBqPZY78xdz563Jrk3I0Nv2iBV+YqGGvrW5LJE38FKYF/Wkl/eYwLVr1WG7LPMO4kVkSXxbTNrmoMzM0Iebilq/ILo1sY0tsw7bXfkLjqfq2yE+jt5mfreffY9Eq2cnVNhYW2CJTC3TZWV5jLhAjGqw7jw8QkoutMDpZMxSzceLfxLTQfUiCgLgKpfdgT1dr5Tn3E/cmu4u0YpyqxSG03hcXlrlaHi7yi2c7q02+i8cEfMa7Mv+YrojPxwAAAAAAAAAAAAAAAAAAAPSPfwBbfFMb/xNrvgAAAABJRU5ErkJggg==",
"fieldvalue":"",
"required":true
}
]
}
]
}
The output should look like this:
enter image description here
Check this approach
You should keep 2 different states for the form data and other stuff such as title, sub title, etc.
I am trying to implement a two-page form in React. On the first page I request a code that I use to fetch some data that I need to display on the second page. Here, by page I mean a different view on the same page. The problem that I am facing is that when I receive a response from the backend API, I use state to update the current state of the fields and this does not seem to happen immediately when I need it. When I change the value of the property that decides which view to render, the fields are still empty, not updating with the values fetched from the API. Now, I know that setState is asynchronous, but I don't know how to handle this situation, since the data fetch is done on demand, not at the beginning, so I don't think I can use useEffect for this job.
Here is my state:
const [state, setState] = useState({
name: "",
cif: "",
address: "",
phone: "",
fax: "",
managerName: "",
email: "",
password: "",
});
const [companyDataFetched, setCompanyDataFetched] = useState(false);
And here are the two views and how I switch between them:
{!companyDataFetched ? (
<CustomForm layout={theme.layout} widthelem={"70%"}>
<Form.Item
name="Company CIF:"
label="Company CIF:"
rules={[
{
pattern: new RegExp(/^\d{2,10}$/),
message: "Invalid CIF!",
},
{
required: true,
message: "Please insert the CIF of the company!",
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "cif")}
/>
</Form.Item>
<CustomButton
backgroundcolor={theme.primaryColor}
textcolor={theme.white}
onClick={() => {
axios.get(`${process.env.REACT_APP_API_URL}/Company/GetByCUI/${state.cif}`)
.then((res) => {
const companyInfo = res.data;
if (companyInfo.name) {
setState((prevState) => {
return {
...prevState,
name: companyInfo.name
};
});
}
if (companyInfo.address) {
setState((prevState) => {
return {
...prevState,
"address": companyInfo.address,
};
});
}
if (companyInfo.phone) {
setState((prevState) => {
return {
...prevState,
"phone": companyInfo.phone,
};
});
}
if (companyInfo.fax) {
setState((prevState) => {
return {
...prevState,
"fax": companyInfo.fax,
};
});
}
setCompanyDataFetched(true);
})
.catch((error) => {
console.log(error);
});
}}
margintop={"13%"}
marginbottom={"13%"}
>
Continue
</CustomButton>
</CustomForm>
) : (
<CustomForm layout={theme.layout} widthelem={"70%"}>
<Form.Item
name="Company name:"
label="Company name:"
rules={[
{
required: true,
message: "Please insert the name of the company!",
whitespace: true,
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "name")}
value={state.name}
/>
</Form.Item>
<Form.Item
name="Company CIF:"
label="Company CIF:"
rules={[
{
pattern: new RegExp(/^\d{2,10}$/),
message: "Invalid CIF!",
},
{
required: true,
message: "Please insert the CIF of the company!",
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "cif")}
value={state.cif}
disabled={true}
/>
</Form.Item>
<Form.Item
name="Address:"
label="Address:"
rules={[
{
required: true,
message: "Please insert the address of the company!",
whitespace: true,
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "address")}
value={state.address}
/>
</Form.Item>
<Form.Item
name="Phone:"
label="Phone:"
rules={[
{
required: true,
message:
"Please insert the phone number of the company!",
whitespace: true,
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "phone")}
value={state.phone}
/>
</Form.Item>
<Form.Item
name="Fax:"
label="Fax:"
rules={[
{
required: true,
message: "Please insert the fax of the company!",
whitespace: true,
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "fax")}
value={state.fax}
/>
</Form.Item>
<Form.Item
name="Manager name:"
label="Manager name:"
rules={[
{
required: true,
message:
"Please, insert the name of the company manager!",
whitespace: true,
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) =>
handleChange(event, "managerName")
}
/>
</Form.Item>
<Form.Item
name="Manager e-mail:"
label="Manager e-mail:"
rules={[
{
type: "email",
message: "Invalid e-mail!",
},
{
required: true,
message:
"Please insert the e-mail of the company manager!",
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "email")}
/>
</Form.Item>
<Form.Item
name="Password:"
label="Password:"
rules={[
{
required: true,
message: "Please, insert a password!",
},
{
min: 4,
message: "Password needs to be at least 4 characters long",
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "password")}
type={"password"}
onKeyPress={verifyCredentials}
/>
</Form.Item>
<CustomButton
backgroundcolor={theme.primaryColor}
textcolor={theme.white}
onClick={() => {
signup();
}}
margintop={"13%"}
marginbottom={"13%"}
>
Register
</CustomButton>
</CustomForm>
)}
The major problem is that when companyDataFetched becomes true, the state containing the field values is not updated and the values of the fields don't update with the data fetched from the backend. How can I handle this issue?
[UPDATE AFTER PAIMAN'S SUGGESTION]
I tried using useEffect and another state for the API response, but the result is the same.
Here is my updated code:
useEffect(() => {
if (companyDataFetched) {
console.log(response);
setState((prevState) => {
return {
...prevState,
nume: response.name,
adresa: response.address,
telefon: response.phone,
fax: response.fax
}
});
}
}, [companyDataFetched, response]);
axios.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`)
.then((res) => {
setResponse(res.data);
setCompanyDataFetched(true);
})
.catch((error) => {
console.log(error);
});
[UPDATE AFTER SANGEET'S SUGGESTION]
I tried using what you suggested. It did not work straight-forward, so I made some changes, but it doesn't work yet.
Here is my updated code:
useEffect(() => {
const fetchData = () => {
return axios
.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`)
.then((res) => {
return res;
})
.catch((error) => {
console.log(error);
});
};
let isMounted = true;
if (companyDataFetched) {
(async () => {
const response = await fetchData();
if (isMounted) {
setState((prevState) => {
return {
...prevState,
name: response.data.name,
address: response.data.address,
phone: response.data.phone,
fax: response.data.fax,
};
});
} else {
setCompanyDataFetched(false);
}
})();
}
return function () {
isMounted = false;
};
}, [companyDataFetched, state.cif]);
Don't have your API call in the onClick handler. Instead trigger the fetch in the button by saying setCompanyDataFetch(true).
Then have a useEffect with companyDataFetch as a dependency.
function fetchData() {
return axios.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`)
.then((res) => {
// setResponse(res.data);
// setCompanyDataFetched(true);
return res;
})
.catch((error) => {
console.log(error);
});
}
Your useEffect looks like so
useEffect(() => {
let isMounted = true;
if (companyDataFetched) {
(async () => {
const response = await fetchData()
if (isMounted) setState((prevState) => {
return {
...prevState,
nume: response.name,
adresa: response.address,
telefon: response.phone,
fax: response.fax
}
});
if (isMounted) setCompanyDataFetched(false);
})();
}
return function () {
isMounted = false;
};
}, [companyDataFetched]);
[UPDATED!] multi-step form implementation. pretend you get the phone number in step 1 then email in step 2.
// component.js
export default function MultiForm(){
const [step , setStep] = useState(1);
const [inputs , setInputs] = useState({
phone : '',
email : ''
});
const handleStep1 = () => {
// validation
if(inputs.phone !== ""){
// set step 2
setStep(2)
}
}
}
const handleStep2 = async() => {
// validation
if(inputs.email !== ""){
const { data , status } = await axios.post(url ,{
phone : inputs.phone,
email : inputs.email
})
if(status === 200){
alert('Registered successfully')
}
}
}
}
return (
<>
{step === 1 && (
<>
<input name="phone" onChange={(e) =>{
setInputs({...inputs , [e.target.name] : e.target.value})
}} />
<button onClick={handleStep1}>Continue</button>
</>
)}
{step === 2 && (
<input name="email" onChange={(e) =>{
setInputs({...inputs , [e.target.name] : e.target.value})
}} />
<button onClick={handleStep2}>Submit</button>
)}
</>
)
}
After a lot of research, I discovered that I was setting the input fields values in a wrong way. Apparently, when using antd Form, it must be initialized and used its reference to dynamically change Form.Item values, like so:
const [form] = Form.useForm();
const [step, setStep] = useState(1);
const [state, setState] = useState({
name: "",
cif: "",
address: "",
phone: "",
fax: "",
contactName: "",
email: "",
password: "",
});
const fetchCompanyData = async () => {
const response = await axios.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`);
setState((prevState) => {
return {
...prevState,
nume: response.data.denumire,
adresa: response.data.adresa,
telefon: response.data.telefon,
fax: response.data.fax,
};
});
form.setFieldsValue({
nume: response.data.denumire,
cif: state.cif,
adresa: response.data.adresa,
telefon: response.data.telefon,
fax: response.data.fax,
});
setStep(2);
};
};
[...]
{step === 1 && (
<CustomForm form={form} layout={theme.layout} widthelem={"70%"}>
<Form.Item
name="Company name:"
label="Company name:"
rules={[
{
pattern: new RegExp(/^\d{2,10}$/),
message: "Invalid CIF!",
},
{
required: true,
message: "Please insert the company CIF!",
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "cif")}
/>
</Form.Item>
<CustomButton
backgroundcolor={theme.primaryColor}
textcolor={theme.white}
onClick={fetchCompanyData}
margintop={"13%"}
marginbottom={"13%"}
>
Continue
</CustomButton>
<CustomForm>
)}
{step === 2 && (
<CustomForm form={form} layout={theme.layout} widthelem={"70%"}>
<Form.Item
name="Company name:"
label="Company name:"
rules={[
{
required: true,
message: "Please insert the name of the company!",
whitespace: true,
},
]}
>
<CustomInput
backgroundcolor={theme.white}
onChange={(event) => handleChange(event, "name")}
/>
</Form.Item>
[...]
<CustomForm>
)}
I am trying to write tests for an authentication form I created. I have the need to mock the return value of a custom hook which returns the login function and the state showing if the login function is running and waiting for a response. I have to mock the useAuth() hook in the top of the component. I've never done testing before so I feel a bit lost!
Here is my Form:
export const LoginForm = () => {
const { login, isLoggingIn } = useAuth(); // here's the function I want to mock
const { values, onChange }: any = useForm({});
const [error, setError] = useState(null);
const handleLogin = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
await login(values);
} catch (err: any) {
setError(err.message);
}
};
return (
<Box
sx={{
py: (t) => t.spacing(8),
px: (t) => t.spacing(8),
background: '#FFF',
borderRadius: 2,
marginTop: 0,
position: 'relative',
zIndex: 1,
}}
>
<form data-testid='login-form' onSubmit={handleLogin}>
<FormTextField
placeholder='your email'
name='email'
data-testid='login-email'
onChangeFn={onChange}
loading={isLoggingIn}
fullWidth={true}
id='email'
label='email'
type='text'
autoFocus={true}
/>
<FormTextField
placeholder='password'
name='password'
data-testid='login-password'
onChangeFn={onChange}
loading={isLoggingIn}
fullWidth={true}
id='password'
label='password'
type='password'
autoFocus={false}
sx={{
marginBottom: 2,
}}
/>
<Typography
variant='subtitle1'
sx={{
marginY: (t) => t.spacing(3.7),
textAlign: 'center',
}}
>
Forget password? <BlueLink to='#/hello' text='Reset here' />
</Typography>
<FormButton
text='Submit'
data-testid='submit-login'
variant='contained'
disabled={false}
fullWidth
type='submit'
sx={{
color: 'white',
}}
/>
<div data-testid='error-login'>
{error && (
<>
<hr />
{error}
</>
)}
</div>
</form>
<Hidden mdDown>
<img
style={{
position: 'absolute',
right: -50,
bottom: -50,
}}
src={SquigglesOne}
alt=''
/>
</Hidden>
</Box>
);
};
export default LoginForm;
Here's how I'm trying to mock it:
jest.mock('../../../helpers/auth', () => ({
...jest.requireActual('../../../helpers/auth'),
useAuth: jest.fn(() => ({
login: () => true,
})),
}));
When I run my tests I get an error:
Error: Uncaught [TypeError: Cannot destructure property 'login' of '(0 , _auth.useAuth)(...)' as it is undefined.] But when I remove the mock my test runs successfully so I hope I'm close. Here's my full test:
import {
fireEvent,
render /* fireEvent */,
within,
} from '#testing-library/react';
import { AuthProvider } from 'helpers/auth';
import { ReactQueryProvider } from 'helpers/react-query';
import { MemoryRouter } from 'react-router';
import LoginForm from './index';
jest.mock('../../../helpers/auth', () => ({
...jest.requireActual('../../../helpers/auth'),
useAuth: jest.fn(() => ({
login: () => true,
})),
}));
const setup = () => {
const loginRender = render(
<MemoryRouter>
<ReactQueryProvider>
<AuthProvider>
<LoginForm />
</AuthProvider>
</ReactQueryProvider>
</MemoryRouter>
);
const emailbox = loginRender.getByTestId('login-email');
const passwordBox = loginRender.getByTestId('login-password');
const emailField = within(emailbox).getByPlaceholderText('your email');
const passwordField = within(passwordBox).getByPlaceholderText('password');
const form = loginRender.getByTestId('login-form');
return {
emailField,
passwordField,
form,
...loginRender,
};
};
test('Input should be in document', async () => {
const { emailField, passwordField } = setup();
expect(emailField).toBeInTheDocument();
expect(passwordField).toBeInTheDocument();
});
test('Inputs should accept email and password strings', async () => {
const { emailField, passwordField } = setup();
emailField.focus();
await fireEvent.change(emailField, {
target: { value: 'atlanteavila#gmail.com' },
});
expect(emailField.value).toEqual('atlanteavila#gmail.com');
passwordField.focus();
await fireEvent.change(passwordField, {
target: { value: 'supersecret' },
});
expect(passwordField.value).toEqual('supersecret');
await fireEvent.keyDown(form, { key: 'Enter' });
});
test('Form should be submitted', async () => {
const { emailField, passwordField, form } = setup();
emailField.focus();
await fireEvent.change(emailField, {
target: { value: 'atlanteavila#gmail.com' },
});
expect(emailField.value).toEqual('atlanteavila#gmail.com');
passwordField.focus();
await fireEvent.change(passwordField, {
target: { value: 'supersecret' },
});
await fireEvent.keyDown(form, { key: 'Enter' });
expect(passwordField.value).toEqual('supersecret');
});
I am unable to get the values of email and password from the FormControl Input values. I am using the BaseWeb ReactUI framework for the field UI.
Need help stuck on this issue.
import { FormControl } from 'baseui/form-control';
import { Input } from 'baseui/input';
import { useStyletron } from 'baseui';
import { Alert } from 'baseui/icon';
import { Button } from 'baseui/button';
import { TiUserAddOutline } from "react-icons/ti";
import Axios from "axios";
import { useHistory, Link } from 'react-router-dom';
import { validate as validateEmail } from 'email-validator'; // add this package to your repo with `$ yarn add email-validator`
import { Select } from 'baseui/select';
import { Checkbox } from 'baseui/checkbox';
function SelectAtStart(props) {
const [css] = useStyletron();
return (
<div className={css({ display: 'flex' })}>
<div className={css({ width: '200px', paddingRight: '8px' })}>
<Select
options={props.options}
labelKey="id"
valueKey="gender"
onChange={({ value }) => props.onSelectChange(value)}
value={props.selectValue}
id={props.id}
/>
</div>
<Input
onChange={e => props.onInputChange(e.target.value)}
value={props.inputValue}
/>
</div>
);
}
function Negative() {
const [css, theme] = useStyletron();
return (
<div
className={css({
display: 'flex',
alignItems: 'center',
paddingRight: theme.sizing.scale500,
color: theme.colors.negative400,
})}
>
<Alert size="18px" />
</div>
);
}
export function RegisterFields() {
const history = useHistory();
const [css, theme] = useStyletron();
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const [checked, setChecked] = React.useState(true);
const [startInputValue, setStartInputValue] = React.useState('');
const [startSelectValue, setStartSelectValue] = React.useState(
[],
);
const [isValid, setIsValid] = React.useState(false);
const [isVisited, setIsVisited] = React.useState(false);
const shouldShowError = !isValid && isVisited;
const onChangeEmail = ({ target: { email } }) => {
setIsValid(validateEmail(email));
setEmail(email);
};
const onChangePassword = ({ target: { password } }) => {
setIsValid(true);
setPassword(password);
}
const handleSubmit = (event) => {
event.preventDefault();
console.log(email, password)
Axios.defaults.headers.common = {
"Content-Type": "application/json"
}
Axios.post("http://localhost:5000/api/signup", {
email: email,
password: password,
firstName: startInputValue,
lastname: startInputValue,
agreement: checked
}).then((res) => {
if (res.status === 200) {
const path = '/dashboard'
history.push(path)
console.log(res)
}
else {
console.log("Unable to create account")
}
})
}
return (
<form
onSubmit={handleSubmit}
className={css({
marginTop: theme.sizing.scale1000,
})}
>
<FormControl
label="Your Email"
error={
shouldShowError
? 'Please input a valid email address'
: null
}
>
<Input
id="email"
value={email}
onChange={onChangeEmail}
onBlur={() => setIsVisited(true)}
error={shouldShowError}
overrides={shouldShowError ? { After: Negative } : {}}
type="email"
required
/>
</FormControl>
<FormControl
label="Your Password"
error={
shouldShowError
? 'Your password is incorrect'
: null
}
>
<Input
id="password"
value={password}
onChange={onChangePassword}
onBlur={() => setIsVisited(true)}
error={shouldShowError}
overrides={shouldShowError ? { After: Negative } : {}}
type="password"
required
/>
</FormControl>
<FormControl
label="Your Full Name"
>
<SelectAtStart
inputValue={startInputValue}
onInputChange={v => setStartInputValue(v)}
selectValue={startSelectValue}
onSelectChange={v => setStartSelectValue(v)}
options={[
{ id: 'Mr', gender: 'Male' },
{ id: 'Mrs', gender: 'Women' },
{ id: 'Ms', gender: 'Female' },
{ id: 'None', gender: 'Dont Say' },
]}
id="start-id"
/>
</FormControl>
<FormControl>
<Checkbox
checked={checked}
onChange={() => setChecked(!checked)}
>
<div className={css({
color:'grey'
})}
>I hereby agree to the terms and conditons of the platform.</div>
</Checkbox>
</FormControl>
<Button type="submit">
<TiUserAddOutline style={{ marginRight: '10px' }} />
Create Account
</Button>
<div>
<p style={{ marginTop: '10px', color: 'grey' }} >To go back to login, please <Link to='/'>click here</Link></p>
</div>
</form>
);
}
I believe it's because you keep setting it to the email/password value of target
which is "undefined".
e.target.email = undefined
e.target.password = undefined
Try with this approach:
const onChangeEmail = e =>
{
setIsValid(validateEmail(e.target.value));
setEmail(e.target.value);
};
const onChangePassword = e =>
{
setIsValid(true);
setEmail(e.target.value);
};
Here you can see that all text inputs are stored in a value of targeted event ( event.target.value )
I am trying to create an edit form using react and data stored in redux. The form helps a user update their profile but my form seems to be read only. The values i pass to the form are displaying well, but nothing happens when i change the input and try to update the state. Any recommendations/assistance on how to update the previous values stored will be appreciated.
My code :
export default function UpdateProfileSection() {
const dispatch = useDispatch();
const [name, setName] = React.useState("");
const [username, setUserName] = React.useState("");
const [email, setEmail] = React.useState("");
const { user: currentUser } = useSelector((state) => state.auth);
if(!currentUser) {
return <Redirect to="/login-page" />;
}
let nme = currentUser.name;
let em = currentUser.email;
let uname = currentUser.username;
let id = currentUser.id;
const handleProfileUpdate = (e) => {
setshowloader(true)
dispatch(update_profile(name, username, email, role, id))
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error)
})
}
return (
<div>
<Label as="a" color="red" size="huge" ribbon style={{ marginBottom: 50 }}>
Update Profile
</Label>
<CustomInput
id="first"
formControlProps={{
fullWidth: true
}}
inputProps={{
value: nme,
type: "text",
onChange: event => {
const value = event.target.value;
setName(value);
}
}}
/>
<CustomInput
labelText="User Name..."
id="user"
formControlProps={{
fullWidth: true
}}
inputProps={{
value: uname,
type: "text",
onChange: event => {
const value = event.target.value;
setUserName(value);
}
}}
/>
<CustomInput
labelText="Email..."
id="Email"
formControlProps={{
fullWidth: true
}}
inputProps={{
value: em,
type: "text",
onChange: event => {
const value = event.target.value;
setEmail(value);
}
}}
/>
<GridItem style={{ textAlign: "center", marginTop: 10 }}>
<Button
color="danger"
size="lg"
onClick={handleProfileUpdate}>
Save Changes
</Button>
</GridItem>
</div>
);
}
The code use nme as value, and this value is the current user in the store. When you write on the input, the value doesn't change, it still being the current user info.
You can try to make an initialization by using useEffect hook, as the following:
I am trying to create an edit form using react and data stored in redux. The form helps a user update their profile but my form seems to be read only. The values i pass to the form are displaying well, but nothing happens when i change the input and try to update the state. Any recommendations/assistance on how to update the previous values stored will be appreciated.
My code :
export default function UpdateProfileSection() {
const dispatch = useDispatch();
const [name, setName] = React.useState("");
const [username, setUserName] = React.useState("");
const [email, setEmail] = React.useState("");
const { user: currentUser } = useSelector((state) => state.auth);
if(!currentUser) {
return <Redirect to="/login-page" />;
}
useEffect( () => {
setName(currentUser.name);
setEmail(currentUser.email);
setUserName(currentUser.username);
}, [])
let id = currentUser.id;
const handleProfileUpdate = (e) => {
setshowloader(true)
dispatch(update_profile(name, username, email, role, id))
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error)
})
}
return (
<div>
<Label as="a" color="red" size="huge" ribbon style={{ marginBottom: 50
}}>
Update Profile
</Label>
<CustomInput
id="first"
formControlProps={{
fullWidth: true
}}
inputProps={{
value: name,
type: "text",
onChange: event => {
const value = event.target.value;
setName(value);
}
}}
/>
<CustomInput
labelText="User Name..."
id="user"
formControlProps={{
fullWidth: true
}}
inputProps={{
value: username,
type: "text",
onChange: event => {
const value = event.target.value;
setUserName(value);
}
}}
/>
<CustomInput
labelText="Email..."
id="Email"
formControlProps={{
fullWidth: true
}}
inputProps={{
value: email,
type: "text",
onChange: event => {
const value = event.target.value;
setEmail(value);
}
}}
/>
<GridItem style={{ textAlign: "center", marginTop: 10 }}>
<Button
color="danger"
size="lg"
onClick={handleProfileUpdate}>
Save Changes
</Button>
</GridItem>
</div>
);
}
Note that the useEffect is executed only one time when the component is mounted, and use the state values to put in the inputs. This can fix the error