Selecting input element form of a react functional component - reactjs

could u help a curious intern developer? ^^
I'm using the form inside a functional react component, there is 4 "fake pages" that Im controlling by buttons to hide/show the others input elements. Everytime the user changes the page by clicking in "Next Page" button Im calling a function to see if the inputs present on that page is correctly filled, If something is missing I wanna show an alert and focus (for example) to the corresponding input.
I already tried to use Jquery but got error saying that $(..) is not defined.
I read in some pages that I could use Refs to make this happen but yet in these pages says that its not indicated to use too much refs in a component, now Im wondering if there is another way to accomplish my goal.
Whats the best way to do this?
My code:
const handlePageChange = (e, pageNumber, action) => {
if(action === '+'){
if(pageNumber === '1'){
const nameArray = contactName.split('' );
if(nameArray.length < 2 || nameArray[1] == ''){
alert('Please inform your full name.');
//$("#contactName").focus();
}else{setPageNumber('2')}
}
}
}
return (
<div className="row">
<form onSubmit={handleSubmit} className="col s12">
{pageNumber==='1' &&
<div id='contact-section' className="row form-section">
<label className='active form-tittle'>Informações de Contato (1/4)</label>
<div className="input-field col s12 ">
<input id="contactName" type="text" className="validate"
value={contactName} onChange={e => setContactName(e.target.value)}
/>
<label className='active' htmlFor="contactName">Nome</label>
</div>
<div className="input-field col s12">
<input id="contactPhones" type="text" className="validate"
value={contactPhones} onChange={e => setContactPhones(e.target.value)}
/>
<label className='active' htmlFor="contactPhones">Telefone de Contato</label>
</div>
<div className="input-field col s12">
<input id="contactEmail" type="email" className="validate"
value={contactEmail} onChange={e => setContactEmail(e.target.value)}
/>
<label className='active' htmlFor="contactEmail">E-mail</label>
</div>
<div className='buttons-box'>
<a className="waves-effect waves-light yellow darken-2 btn" onClick={e=> handlePageChange(e, '1','+')}><i className="material-icons right">chevron_right</i>Next Page</a>
</div>
</div>}

In React, given the advent of JSX, you don't really need to use JQuery.
If you are planning to fully leverage the power of React, or maybe you're confused on why you don't really need JQuery, I suggest to read up on this article...
Refs provide a way to access DOM nodes or React elements created in
the render method.
Now, just taking a look at your code snippet, I suggest you take a different approach. Instead of wrapping all three of your "fake page" components in a form, think about having an entire form per "fake page" (three forms total if you have three pages). Note that you don't have to actually perform a form submit when they hit the next button, but you could just intercept that event, perform validation, accumulate the values into a React context or higher level hook, and render the next page.
With React, (just like other things) you have to learn to think in React. Make sure that you hash out different approaches when you run into problems like this, because if your code gets too spaghetti-like with a bunch of refs pointing everywhere, there is likely a simpler, more elegant approach.
If you are using React.js, I would be inclined to suggest formik or some framework to abstract away some of the complexity of dealing with form state and validation. It will make your life a whole lot easier. I personally use Ant Design's Steps with each Step containing a form. You can always shell out to scss, tailwindcss, less, or the style prop to override Ant Design's classic styles.
Here is an untested example I threw together using Ant Design just so you can understand conceptually where I am coming from.
import React, { useState } from "react";
import { Form, Button, Divider, Steps, message } from "antd";
import { PageOne, PageTwo, PageThree } from "./Pages";
const { Step } = Steps;
function StepperForm() {
const [current, setCurrent] = React.useState(0);
const [globalValues, setGlobalValues] = useState([]);
const [PageOneForm] = Form.useForm();
const [PageTwoForm] = Form.useForm();
const [PageThreeForm] = Form.useForm();
const steps = [
{
title: "Page One",
content: <PageOne formRef={PageOneForm} />,
},
{
title: "Page Two",
content: <PageTwo formRef={PageTwoForm} />,
},
{
title: "Page Three",
content: <PageThree formRef={PageThreeForm} />,
},
];
const prev = () => {
setCurrent(current - 1);
var it = globalValues;
it.pop();
setGlobalValues(it);
};
const next = () => {
switch (current) {
case 0:
PageOneForm.validateFields()
.then((values) => {
setGlobalValues([values]);
setCurrent(current + 1);
})
.catch((errorInfo) => {
message.error(
"You cannot move on until all " +
errorInfo.errorFields.length +
" errors are resolved."
);
});
break;
case 1:
PageTwoForm.validateFields()
.then((values) => {
var it = onBoardValues;
it.push(values);
setGlobalValues(it);
setCurrent(current + 1);
})
.catch((errorInfo) => {
message.error(
"You cannot move on until all " +
errorInfo.errorFields.length +
" errors are resolved."
);
});
break;
case 2:
PageThreeForm.validateFields()
.then((values) => {
var it = onBoardValues;
it.push(values);
setGlobalValues(it);
setCurrent(current + 1);
//call api.
})
.catch((errorInfo) => {
message.error(
"You cannot move on until all " +
errorInfo.errorFields.length +
" errors are resolved."
);
});
break;
}
};
return (
<>
<Steps current={current}>
{steps.map((item) => (
<Step key={item.title} title={item.title} />
))}
</Steps>
<Divider />
<div>{steps[current].content}</div>
<div>
{current > 0 && <Button onClick={() => prev()}>Previous</Button>}
{current < steps.length - 1 && (
<Button onClick={() => next()}>Next</Button>
)}
{current === steps.length - 1 && (
<Button onClick={() => next()}>Done</Button>
)}
</div>
</>
);
}
Where the PageOne component might look like
import React from "react";
import { Form, Input, Row, Col } from "antd";
export function PageOne(props) {
const [form] = Form.useForm();
return (
<div>
<Form
size={"medium"}
form={props.formRef}
name="PageOne"
label="left"
layout="vertical"
>
<Row>
<Col>
<Form.Item
validateTrigger="onBlur"
name="FirstName"
label={"First Name"}
rules={[
{
required: true,
message: "Please enter your first name.",
whitespace: true,
},
]}
>
<Input />
</Form.Item>
</Col>
<Col>
<Form.Item
validateTrigger="onBlur"
name="LastName"
label={"Last Name"}
rules={[
{
required: true,
message: "Please enter your last name.",
whitespace: true,
},
]}
>
<Input />
</Form.Item>
</Col>
</Row>
<Row>
<Col>
<Form.Item
validateTrigger="onBlur"
name="EmailAddress"
label="Primary E-mail"
rules={[
{
type: "email",
message: "Enter a valid E-mail.",
},
{
required: true,
message: "Please enter your E-mail.",
},
]}
>
<Input />
</Form.Item>
</Col>
<Col>
<Form.Item
validateTrigger="onBlur"
name="PhoneNumber"
label="Phone Number"
hasFeedback
rules={[
{
required: true,
message: "Phone number required for MFA.",
},
({ setFieldsValue }) => ({
validator(_, value) {
if (!value) {
return Promise.reject();
}
value = value.toString();
value = value.replace(/[^\d]/g, "");
setFieldsValue({ PhoneNumber: value });
if (value.length < 10) {
return Promise.reject(
"Phone number can't be less than 10 digits"
);
}
if (value.length > 10) {
return Promise.reject(
"Phone number can't be more than 10 digits"
);
}
return Promise.resolve();
},
}),
]}
>
<Input />
</Form.Item>
</Col>
</Row>
<Row>
<Col>
<Form.Item
validateTrigger="onBlur"
name="Password"
label="Password"
rules={[
{
required: true,
message: "Please enter your password.",
},
]}
hasFeedback
>
<Input.Password />
</Form.Item>
</Col>
<Col>
<Form.Item
validateTrigger="onBlur"
name="ConfirmPassword"
label="Confirm Password"
dependencies={["password"]}
hasFeedback
rules={[
{
required: true,
message: "Please confirm your password.",
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("Password") === value) {
return Promise.resolve();
}
return Promise.reject(
"The two passwords that you entered do not match."
);
},
}),
]}
>
<Input.Password />
</Form.Item>
</Col>
</Row>
</Form>
</div>
);
}
As you can see, component frameworks help you out a lot (even with form validation). I personally like how ANTD has it included. If you are just learning, there is some value in re-creating the wheel for learning's sake, but after a while, it is important to learn that you don't need to every time.
P.S. Next time, include your complete component code formatted. It makes it hard for us to read and comprehend otherwise.

Related

Previous state rendering in Chat thread React JS

I have created a form which has two dropdowns when I select both of them and click on Add Story Button then a chat thread is added.
The problem is when I select New Intent Name and New Action Name from dropdown then the previous Action Name also gets added I don't want like that what I want is
Problem:-
One Intent Name can Have multiple Actions But not duplicate actions
I think I am not setting or mapping the state variable properly, please guide me as of where am I going wrong
While I am able to manage the 1st point I want help with the below two
My Code
import React, { useEffect, useState } from "react";
import {
Form,
Input,
Button,
Select,
Card,
Typography,
notification,
} from "antd";
import { Layout } from "antd";
const { Header, Footer, Content } = Layout;
const { Text } = Typography;
const { Option } = Select;
const CreateStory = () => {
const [form] = Form.useForm();
const [storyValue, setStoryValue] = useState("")
const [intentName, setIntentName] = useState([])
const [actionName, setActionName] = useState([])
const [valueIntent, setValueIntent] = useState("")
const [valueAction, setValueAction] = useState("")
const [results,setResults] = useState([])
const [uniqueResults,setUniqueResults] = useState([])
const storyInputValue = (e) => {
setStoryValue(e.target.value);
};
const onFinish = (values) => {
// console.log("Success:", values);
};
const onFinishFailed = (errorInfo: any) => {
console.log("Failed:", errorInfo);
};
const SelectIntentName = (valueIntent) => {
setValueIntent(valueIntent)
console.log(valueIntent)
};
const SelectActionName = (valueAction) => {
// console.log(valueAction)
setValueAction(valueAction);
setActionName(prev => [...prev,valueAction])
};
// Error Notification
const openNotificationWithIcon = (type) => {
notification[type]({
message: "intent name cannot be empty",
});
};
const addStory = () => {
// setActionName(prev => [...prev,valueAction])
results.push({
intent_name: valueIntent,
// if valueAction is already present then don't push it into the array
actions: [...new Set(actionName)]
})
const removedDup = ([...new Map(results.map((item, key) => [item["intent_name"], item])).values()])
// setUniqueResults(prev => removedDup)
setUniqueResults(removedDup)
}
console.log(uniqueResults)
return (
<div className="csi-create-story-component-page-0103CS">
<Card
title="Create Story"
className="csi-create-story-screen-card-0104SC"
size="small"
>
<Form
onFinish={onFinish}
onFinishFailed={onFinishFailed}
layout="vertical"
>
<Form.Item
label="Story Name"
name="Story Name"
rules={[
{ required: true, message: "Please input your story name!" },
]}
>
<Input
value={storyValue}
onChange={storyInputValue}
placeholder="Enter story name"
/>
</Form.Item>
<div className="csi-action-intent-box-grid-column-0126">
<Form.Item
label="Intent Name"
name="Intent Name"
rules={[
{ required: true, message: "Please select your intent name!" },
]}
>
<Select
placeholder="Select a option"
allowClear
showSearch
onSelect={SelectIntentName}
>
<Option value="intent_name_1">intent_name_1</Option>
<Option value="intent_name_2">intent_name_2</Option>
<Option value="intent_name_3">intent_name_3</Option>
</Select>
</Form.Item>
<Form.Item
label="Action Name"
name="Action Name"
rules={[
{ required: true, message: "Please select your action name!" },
]}
>
<Select
placeholder="Select a option"
allowClear
showSearch
onSelect={SelectActionName}
>
<Option value="action_name_1">action_name_1</Option>
<Option value="action_name_2">action_name_2</Option>
<Option value="action_name_3">action_name_3</Option>
</Select>
</Form.Item>
</div>
<Form.Item>
<Button type="primary" htmlType="submit" onClick={addStory}>
ADD STORY
</Button>
</Form.Item>
</Form>
</Card>
<div>
<Layout className="csi-created-story-list-screen-card-0105SLS">
<Header>{storyValue}</Header>
<Content className="csi-intent-action-content-layout-0353IA">
<div
className="csi-created-intent-action-parent-box-0237IA"
>
{uniqueResults.map((uniqueResult,index) => {
return(
<div key={index}>
<div className="csi-intent-name-left-box">
<span className="csi-intent-text-com-0245I">
<span className="csi-INTENT-text">Intent</span>
<Text>{uniqueResult.intent_name}</Text>
</span>
</div>
<div className="csi-action-name-right-box">
<span className="csi-action-text-com-0246A">
<span className="csi-ACTION-text">Action</span>
<Text>{uniqueResult.actions[index]}</Text>
</span>
</div>
</div>
)
})}
</div>
{/* <div
className="csi-created-intent-action-parent-box-0237IA"
>
{intentName.map((intentName, index) => {
return (
<>
<div className="csi-intent-name-left-box" key={index}>
<span className="csi-intent-text-com-0245I">
<span className="csi-INTENT-text">Intent</span>
<Text>{intentName}</Text>
</span>
</div>
{actionName.map((actionName, index) => {
return (
<div className="csi-action-name-right-box" key={Math.random().toString()}>
<span className="csi-action-text-com-0246A">
<span className="csi-ACTION-text">Action</span>
<Text>{actionName}</Text>
</span>
</div>
);
})}
</>
)
})}
{actionName.map((actionName, index) => {
return (
<div className="csi-action-name-right-box" key={index}>
<span className="csi-action-text-com-0246A">
<span className="csi-ACTION-text">Action</span>
<Text>{actionName}</Text>
</span>
</div>
);
})}
</div> */}
</Content>
<Footer className="csi-footer-submit-button-for-intent-action-0357">
<Button type="primary">Submit</Button>
</Footer>
</Layout>
</div>
</div>
);
};
export default CreateStory;
It is definitely because of index in key prop. React doesn't know what you want to render there.
Don't use index as key if you're going to change order of items
The main issue relies with your implementation. You can not handle it with two array of strings.
My opinion -
Use a result array which will have items as object -
{
"intent_name": "",
"actions": [] //array of string
}
whenever Add Story button is clicked give an entry to this result array. condition should be like - if intent_name already exist then -> check if object.actions already have entry -> if not then give entry.
and loop through this result array to show the intent action list.
you can also use id instead of name in terms of uniqueness.
overall if you need any clarification then please ping me.
and apologies if I miss anything. I am trying this platform new.

I want to handle error state in Form.Item Ant design. how to handle disable button?

I spend almost 1 week to solve this problem.
I want to disable the button while the field gets an error.
Somehow always the form.getFieldsError() gets Array(0), even the error message this is required or should be 7 digit was showed up.
If you have any idea please help me.
this is my code.
import React from 'react';
import { Form, Input, Button } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const DynamicFieldSet = () => {
const MAX = 3;
const [form] = Form.useForm();
const onValuesChange = (changedValues, allValues) => {
console.log(allValues.lines);
};
console.log(
// this is always true
form.getFieldsError().filter(({ errors }) => errors.length).length === 0
);
return (
<Form
initialValues={{ lines: [''] }}
onValuesChange={onValuesChange}
form={form}
name={'lines'}
>
<Form.List name={'lines'}>
{(fields, { add, remove }, { errors }) => {
return (
<div>
{fields.map((field, index) =>
index < MAX ? (
<Form.Item required={false} key={field.key}>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
message: 'this is required',
},
{
min: 7,
max: 7,
message: 'should be 7 digit',
},
]}
noStyle
>
<Input
placeholder=""
style={{ width: '50%' }}
type="number"
/>
</Form.Item>
{index === 0 ? (
<>
<PlusOutlined
onClick={() => {
add();
}}
/>
</>
) : index === MAX - 1 ? (
<MinusCircleOutlined onClick={() => remove(field.name)} />
) : (
<>
<PlusOutlined
onClick={() => {
add();
}}
/>
<MinusCircleOutlined
onClick={() => remove(field.name)}
/>
</>
)}
</Form.Item>
) : null
)}
</div>
);
}}
</Form.List>
<Form.Item>
<Button disabled={false} htmlType="submit">
Send
</Button>
</Form.Item>
</Form>
);
};
export default DynamicFieldSet;
I'm not really sure why but form.getFieldsError() is just not going to work. In order to achieve what you need, you should try using form.validateFields() instead.
The validateFields method, returns a promise that shows all the fields' data or errors on them:
form.validateFields()
.then(values => {
// Do something with values
})
.catch(errors => {
// Do something with errors;
});
Note that this will validate all the fields that you haven't even touched, causing all the provided error messages of all the fields to be shown. So, in case you want to validate just one field or so, all you need to do is to provide an array of field's names like ['myInput'].
From here, just create a new state to be set when errors appear and use it to show/hide-enable/disable your button.

pass formik.values to another js file

I learn react and I am trying to create a stepper to manage my form correct,I'm looking for the best fit way to pass input value from one step to another
The full code is bellow
here the input value from step 1 :
<MDBInput
label="exerciseManager"
name="exerciseManager"
type="text"
value={formik.values.exerciseManager}
onChange={formik.handleChange}
outline
size="lg"
/>
I want to pass formik.values.exerciseManager to step 2 so i can use it there
step 1 and step 2 in different js file
how should I do that correct?
this is step 1 :
I want it will show here :
full step 1 code
//now check that the value get correct to the database
const Certifications = (props) => {
//state //Setstate
const [fieldApproveOptions, setFieldApproveOptions] = useState([])
//useEffect Hooks
useEffect(() => {
axios.get('/api/certifications/fieldApproveOptions?userId=1234567&rank=Colonel&force=Moran')
.then(response => {
console.log(response.data)
setFieldApproveOptions(response.data.fieldApproveOptions)
}
).catch(err => console.log(err))
}, [])
const formik = useFormik({
initialValues: {
exerciseName: '',
//exerciseBy: '', autofill current user from session
exerciseOOB: '',
exercisePOD: '',
exerciseType: '', // options should be pull from db
exerciseLive: '',
fieldApprove: '', // options should be pull from db
fileApprove: '', // options should be pull from db
artilleryApprove: '', // options should be pull from db
exerciseManager: '',
trainerOfficerApprove: '', // options should be pull from db
cerRes: '',
fieldApproveOptions: []
},
onSubmit: values => {
axios.post('/api/certifications', values)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
},
})
return (
<Card>
<CardContent>
<div className="Pedding">
<MDBContainer fluid >
<MDBRow center >
<MDBCol md="4" >
<div className="MDBColor">
<form onSubmit={formik.handleSubmit} autoComplete="off">
<p className="h1 text-center" style={{ paddingTop: "10px", fontWeight: "bold" }}>Certifications</p>
<div className="Certifications">
<MDBInput
label="Exercise name"
name="exerciseName"
type="text"
onChange={formik.handleChange}
value={formik.values.exerciseName}
outline
size="lg"
/>
<MDBInput
label="Exercise type"
name="exerciseType"
list="types"
onChange={formik.handleChange}
value={formik.values.exerciseType}
outline
size="lg"
/>
<datalist id="types" >
<option data-value="1" value="Open Terrain">Open Terrain</option>
<option value="Urban warfare" >Urban warfare</option>
<option value="Armoured fighting vehicle" >Armoured fighting vehicle</option>
<option value="unplanned" >unplanned</option>
<option value="live military exercise" >live military exercise</option>
</datalist>
<MDBInput
label="Order of battle"
name="exerciseOOB"
type="number"
min="20"
onChange={formik.handleChange}
value={formik.values.exerciseOOB}
outline
size="lg"
/>
{/*FieldApprove button */}
<MDBInput
label="fieldApprove"
name="fieldApprove"
list="fieldApproves"
onChange={formik.handleChange}
value={formik.values.fieldApprove}
outline
size="lg"
/>
<datalist id="fieldApproves" defaultValue>
{fieldApproveOptions.map((option, i) =>
<option key={i++} value={option.id}>
{option.rank + " " + option.firstName + " " + option.lastName}
</option>)}
</datalist>
<MDBInput
label="fileApprove"
name="fileApprove"
type="text"
value={formik.values.fileApprove}
onChange={formik.handleChange}
outline
size="lg"
/>
<MDBInput
label="artilleryApprove"
name="artilleryApprove"
type="text"
value={formik.values.artilleryApprove}
onChange={formik.handleChange}
outline
size="lg"
/>
<MDBInput
label="exerciseManager"
name="exerciseManager"
type="text"
value={formik.values.exerciseManager}
onChange={formik.handleChange}
outline
size="lg"
/>
<MDBInput
label="trainerOfficerApprove"
name="trainerOfficerApprove"
type="text"
value={formik.values.trainerOfficerApprove}
onChange={formik.handleChange}
outline
size="lg"
/>
<div style={{ fontSize: "large", fontWeight: "bold" }} className="custom-control custom-checkbox">
<input type="checkbox"
onChange={formik.handleChange}
value={formik.values.exerciseLive}
className="custom-control-input"
name="exerciseLive"
id="live"
value="on"
/>
<label className="custom-control-label" htmlFor="live"> live exercise</label>
</div>
{/*pod section*/}
<span style={{ fontSize: "large", fontWeight: "bold", float: "left" }} >part of the day:</span>
<div className="forms" style={{ fontWeight: "bold" }} onChange={formik.handleChange} value={formik.values.exercisePOD} >
{/*night button*/}
<label htmlFor="rdo1">
<input type="radio" id="rdo1" name="exercisePOD" value="night" />
<span className="rdo"></span>
<span>night</span>
</label>
{/*day button*/}
<label htmlFor="rdo2">
<input type="radio" id="rdo2" name="exercisePOD" value="day" />
<span className="rdo"></span>
<span>day</span>
</label>
</div>
<div className="text-center">
<MDBBtn type="submit" color="yellow">Send</MDBBtn>
</div>
</div >
</form >
</div>
</MDBCol>
</MDBRow>
</MDBContainer >
</div>
</CardContent>
</Card>
);
}
export default Certifications;
here Soform.js full code
const SoForm = () => {
return (
<p >
{formik.values.exerciseManager}
</p>
)
}
export default SoForm;
Here formikStepper code
export default function Home() {
return (
<Card>
<CardContent>
<FormikStepper
initialValues={{
}}
onSubmit={async (values) => {
await sleep(3000);
console.log('values', values);
}}
>
<FormikStep label="Certifications">
<Box paddingBottom={2}>
<Certifications ></Certifications>
</Box>
</FormikStep>
<FormikStep
label="loginPageF"
>
<Box paddingBottom={2}>
<SoForm ></SoForm>
It's really hard to read and understands your code. Try to provide minimal reproducible statements. I can understand you problem, but can't work with your code. So I have created my won example. Here, codesandbox.io : https://codesandbox.io/s/elegant-shtern-362ki?file=/src/App.js
Let me explain it for you.
index.js -> is entry point, that calls only one component App, as we have no route
App.js -> is the parent component, which has two child, Certification and Panel
Certification.js -> takes input
Panel -> renders data from certification
If I'm not wrong, that's what you want. Render data in a component to another component, which are siblings to one another.
App.js :
import React from "react";
import Certifications from "./Certifications";
import Panel from "./Panel";
class App extends React.Component {
state = {
value1: "",
value2: ""
};
fromCert = (name, value) => {
this.setState({ [name]: value });
};
render() {
return (
<div>
{this.state.value1}
{this.state.value2}
<Certifications fromCert={this.fromCert} />
<Panel value1={this.state.value1} value2={this.state.value2} />
</div>
);
}
}
export default App;
Panel.js:
import React from "react";
class Panel extends React.Component {
render() {
return (
<div>
Here we will render data from App, below
<div>{this.props.value1}</div>
<div>{this.props.value2}</div>
Panel ends
</div>
);
}
}
export default Panel;
Certification.js:
import React from "react";
class Certifications extends React.Component {
state = {
input1: "",
input2: ""
};
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
this.props.fromCert(e.target.name, e.target.value);
};
render() {
return (
<div>
<input name="value1" onChange={this.onChange} />
<input name="value2" onChange={this.onChange} />
<div>
Inside certificate
<div>{this.state.input1}</div>
<div>{this.state.input2}</div>
Certificate ends
</div>
</div>
);
}
}
export default Certifications;
First of all, Certifications have two states - value1, value2. So I have given two input's name value1, value2. So that I can set value dynamically. Why? - just imagine I have 10 or 20 states. And that you have in your code.
And onChange of value, I'm triggering a callback to App with fromCert. Try passing 1000+ parameter, it will. :wink:
When I'm triggering that callback from certification, it's getting called in App.js's fromCert. Why is that? Cause I have passed that function or method as parameter - <Certifications fromCert={this.fromCert} />. So that's the reason why this function or method acts as callback. When I get those values, I just set them to App.js's state. Then pass them as props to Panel.
So whatever changes gets applied to Certification that comes to App, which then gets passed to Panel.
Certification -> App : Child to Parent communication
App -> Panel : Parent to Child communication
Certification -> Panel : Child to Child communication / Sibling communication
Try to imagine the hierarchy.
Thanks a lot. If you have any question, feel free to ask.

How to get boolean data from API selected in a Dropdown (REACT)?

I created a dropdown with "true" and "false" options. I get from my API all of my data in a table.
On update, I've got a form that gets the row data. With boolean values, it gets true/false just if I let the html type on "text".
I want to use the dropdown but at the moment it's static so it's always on "true".
How can I select the right value and update the change?
toSelectOption.js
export const trueFalse = [
{ label: "true", value: "Y" },
{ label: "false", value: "F" }
];
Update.jsx
renderTrueFalse() {
return trueFalse.map((item) => {
if (this.state.showKey === true) {
return (
<option value={item.value}>{item.label}</option>
);
}
});
}
//...
<Formik
initialValues={{
//...
showKey,
//...
}}
onSubmit={this.onSubmit}
//...
{
(props) => (
<Form>
//...
<Col md="3">
<FormGroup>
<label>Show Key</label>
<Field className="form-control" component="select"
name="showKey">
{this.renderTrueFalse()}
</Field>
</FormGroup>
</Col>
//...
<div className="d-flex justify-content-center">
<MDBBtn type="submit"
className="btn btn-outline-success waves-effect">Salva</MDBBtn>
</div>
</Form>
)
}
</Formik>
//...
I think that you are missing an onChange event handler on your <Field> component.
Try to add this:
Update.jsx
<Field className="form-control"
component="select"
name="showKey"
onChange={e => this.handleOnChange(e)}
>
and then below your function renderTrueFalse(), add this one:
handleOnChange = e => {
console.log('value->', e.target.value) // e.target.value is your output (Y or F) from the select option
// do whatever you wanna do here with the value from the option in this case ('Y' OR 'F')
}
Open your develop Tools (F12) and see that you are indeed receiving the updated value when you choose the right option.
Check this working on a sample with only React, here

How to get my input validation and disabling/enabling my submit button depending on the state of the form in ReactJS

I am using npm Validator and trying to validate the input info and then change the state of the submit address button depending on the validation. The way I have it set up now I can type and the text shows in the console but not in the UI. I'm not sure what I have wrong. The button will not change to 'enabled' either.
Here is the component:
import React, {Component} from 'react';
import { CardElement, injectStripe } from 'react-stripe-elements';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { clearCart } from '../actions/clearCartAction';
import getTotal from '../helpers/getTotalHelper';
import { Container, Col, Form, FormGroup, Input, Button } from 'reactstrap';
import './StripeCheckoutForm.css';
import validator from 'validator';
const cardElement = {
base: {
color: '#32325d',
width: '50%',
lineHeight: '30px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '18px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
const FIREBASE_FUNCTION = 'https://us-central1-velo-velo.cloudfunctions.net/charge/';
// Function used by all three methods to send the charge data to your Firebase function
async function charge(token, amount, currency) {
const res = await fetch(FIREBASE_FUNCTION, {
method: 'POST',
body: JSON.stringify({
token,
charge: {
amount,
currency,
},
}),
});
const data = await res.json();
data.body = JSON.parse(data.body);
return data;
}
class CheckoutForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
state = {
paymentComplete: false,
firstName: '',
lastName: '',
address: '',
city: '',
prefecture: '',
zipCode: '',
email: '',
submitDisabled: true
}
inputChangeHandler = (event, name) => {
console.log('inputChange', name, event.target.value)
event.preventDefault()
this.setState({
name: event.target.value
}, console.log(this.state.submitDisabled), function(){ this.canSubmit() })
}
canSubmit = () => {
console.log("canSubmit")
const { firstName, lastName, address, city, prefecture,zipCode, email} = this.state
if (validator.isAlpha(firstName)
&& validator.isAlpha(lastName)
&& address > 0
&& validator.isAlpha(city)
&& validator.isAlpha(prefecture)
&& zipCode > 0
&& validator.isEmail(email)) {
this.setState({submitDisabled: false})
} else {
this.setState({submitDisabled: true})
}
}
clearCartHandler = () => {
console.log('clearCartHandler');
this.props.onClearCart()
}
// User clicked submit
async submit(ev) {
console.log("clicked!")
const {token} = await this.props.stripe.createToken({name: "Name"});
const total = getTotal(this.props.cartItems);
const amount = total; // TODO: replace with form data
const currency = 'USD';
const response = await charge(token, amount, currency);
if (response.statusCode === 200) {
this.setState({paymentComplete: true});
console.log('200!!',response);
this.clearCartHandler();
} else {
alert("wrong credit information")
console.error("error: ", response);
}
}
render() {
if (this.state.complete) {
return (
<div>
<h1 className="purchase-complete">Purchase Complete</h1>
<Link to='/'>
<button>Continue Shopping</button>
</Link>
</div>
);
}
return (
<div className="checkout-wrapper">
<Container className="App">
<h2 className='text-center'>Let's Checkout</h2>
<Form className="form">
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="first name"
name={"first name"}
value={this.state.firstName}
id="exampleEmail"
placeholder="first name"
/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="last name"
name="last name"
value={this.state.lastName}
id="exampleEmail"
placeholder="last name"
/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="address"
name="address"
value={this.state.adress}
id="exampleEmail"
placeholder="address"
/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="city"
name="city"
value={this.state.city}
id="exampleEmail"
placeholder="city"
/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="prefecture"
name="prefecture"
value={this.state.prefecture}
id="exampleEmail"
placeholder="prefecture"
/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="zipcode"
name="zipcode"
value={this.state.zipCode}
id="exampleEmail"
placeholder="zipcode"
/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Input
onChange= {this.inputChangeHandler}
type="email"
name="email"
value={this.state.email}
id="exampleEmail"
placeholder="myemail#email.com"
/>
</FormGroup>
</Col>
<Button className="save-address-button" disabled={this.state.submitDisabled}>Submit Address</Button>
<div className="card-element">
<CardElement style={cardElement}/>
</div>
</Form>
<button className="checkout-button" disabled={false} onClick={this.submit}>Submit</button>
</Container>
</div>
);
}
}
const mapStateToProps = state => {
return {
cartItems: state.shoppingCart.cartItems
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClearCart: () => dispatch(clearCart())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(injectStripe(CheckoutForm));
I think the bug is related to the misuse of the callback function of this.setState.
In your inputChangeHandler method, you passed three arguments to this.setState(), however, this.setState() expects you to pass only two arguments at most, with the second argument being the callback function.
Since you pass your callback function as the third argument, this.setState() will silently ignores it, so your validation method is not being called.
Another issue is related to the name parameter for the inputChangeHandler, the inputChangeHandler will only receive the event object, not the name. In order to access the name attribute of the input element, you will need to access it through event.target.name
To solve the issues, you can change the inputChangeHandler to
inputChangeHandler = event => {
const { name, value } = event.target;
console.log('inputChange', event.target.name, event.target.value)
// notice the square bracket, this means we use the computed variable as
// the object key, instead of using the string 'name' as object key
// also the name attribute in your Input element must match your state's key
// use name='lastName' instead of name='last name' as your state's key is lastName
this.setState({
[name]: value
}, this.canSubmit) // changing this line
}
If you want to keep your console.log(this.state.submitDisabled), you can add it inside your validation method.
canSubmit = () => {
console.log("canSubmit", this.state.submitDisabled) // add it here
const { firstName, lastName, address, city, prefecture, zipCode, email} = this.state
if (validator.isAlpha(firstName)
&& validator.isAlpha(lastName)
&& address > 0
&& validator.isAlpha(city)
&& validator.isAlpha(prefecture)
&& zipCode > 0
&& validator.isEmail(email)) {
this.setState({submitDisabled: false})
} else {
this.setState({submitDisabled: true})
}
}
In the render method, I think you mean
if (this.state.paymentComplete)
instead of
if (this.state.complete)
Also, the button at the bottom, you hard-coded the disabled attribute value
<button
className="checkout-button"
disabled={false}
onClick={this.submit}
>
Submit
</button>
maybe you mean
<button
className="checkout-button"
disabled={this.state.submitDisabled}
onClick={this.submit}
>
Submit
</button>
As a side note, the type attribute for the input element should be 'text', 'number', 'checkbox', etc... Instead of some random text like 'last name' or 'prefecture'

Resources