form validation on change blur and submit of form and fields - javascript

// App.js
import React, { Component } from 'react';
import './App.css';
import fields from './fields'
import CustomInputType from './custominputtype'
class App extends Component {
state = {
formData: {},
fieldErrorStatus: {},
submitErrorStatus: false
}
handleChange = (e) => {
// adding the new on change value to the corresponding field name
const { name, value } = e.target;
const tempObj = { ...this.state.formData };
tempObj[name] = value;
this.setState({ formData: tempObj });
// adding the error status for the corresponding field name
let tempErrorStatus = { ...this.state.fieldErrorStatus }
tempErrorStatus[name] = false;
this.setState({ fieldErrorStatus: tempErrorStatus })
};
handleSubmit = (e) => {
let formValues = this.state.formData;
if (Object.keys(formValues).length === 0) {
this.setState({ submitErrorStatus: true })
}
else {
let tempErrorStatus = {};
this.setState({ submitErrorStatus: false });
Object.keys(formValues).forEach(key => {
if (formValues[key]) {
tempErrorStatus[key] = false;
}
})
this.setState(prevState => {
return {
fieldErrorStatus: { ...prevState.fieldErrorStatus, tempErrorStatus }
}
})
}
e.preventDefault();
}
render() {
return (
<div className="form">
<form
onSubmit={this.handleSubmit}
onChange={(e) => this.handleChange(e)}
>
<div className="inputs-collection">
{
fields[0].attributes.map((field, i) => {
return (
<CustomInputType
attributes={field}
key={i}
value={this.state.formData[i]}
obj={this.state.formData}
errorStatus={this.state.fieldErrorStatus}
displayError={this.state.submitErrorStatus}
/>
)
})
}
</div>
<div className="button-container">
<button className="submit-button" type="submit">Submit Details</button>
</div>
</form>
</div>
)
}
}
export default App;
// CustomInputType
import React , {Component} from 'react'
class CustomInputType extends Component {
render(){
const {
attributes: {
id,
name,
dataType,
} = {},
displayError,
obj,
errorStatus
} = this.props;
return (
<div className="input-container">
<label htmlFor={id}>
{name}
</label>
<input
type={dataType}
name={name || ''}
value={obj[name]}
id={id}
/>
{
displayError || Boolean(errorStatus[name]) ?
<span>{`Error on ${name}`}</span> : null
}
</div>
)
}
}
export default CustomInputType
// fields
let fields = [
{
"id": "1",
"name": "Form 1",
"type": "Dynamic Form",
"attributes": [
{
"name": "First Name",
"dataType": "String",
"id": 101,
},
{
"name": "Surname",
"dataType": "String",
"id": 102,
},
{
"name": "Phone Number",
"dataType": "Number",
"id": 103,
},
{
"name": "Roll Number",
"dataType": "Number",
"id": 104,
}
]
}
];
export default fields;
i have a parent component , where i am reading a json file locally and rendering the fields, basically i have a child component which is a custom input type component.
in my child component there is an prop called error it is a boolean value. So if it is true it will show a red box around the field. The cases i need to show the red box are onChange , onBlur and submit.
for sumbit i am using submitErrorStatus variable in state, and for handleChange and onBlur using fieldErrorStatus. So when the user directly submit without any fields entering redbox should come, once he types each field or blur the redbox should go.
i have done the below but some where it is confusing.
Parent Component
state = {
formData : {},
fieldErrorStatus : {},
submitErrorStatus : false
}
handleChange = (e) => {
// adding the new on change value to the corresponding field name
const { name, value} = e.target;
const tempObj = {...this.state.formData};
tempObj[name] = value;
this.setState({ formData:tempObj });
// adding the error status for the corresponding field name
let tempErrorStatus = {...this.state.fieldErrorStatus}
tempErrorStatus[name] = false;
this.setState({fieldErrorStatus:tempErrorStatus})
};
handleSubmit = (e) => {
let formValues = this.state.formData;
if(Object.keys(formValues).length === 0){
this.setState({submitErrorStatus: true})
}
else{
let tempErrorStatus = {};
this.setState({submitErrorStatus: false});
Object.keys(formValues).forEach(key => {
if(formValues[key]){
tempErrorStatus[key] = false;
}
})
this.setState(prevState => {
return {
fieldErrorStatus: {...prevState.fieldErrorStatus, tempErrorStatus}
}
})
}
e.preventDefault();
}
render(){
<div className = "form">
<form
onSubmit = {this.handleSubmit}
onChange = {(e) => this.handleChange(e)}
>
<div className = "inputs-collection">
{
fields.map((field, i) => {
return (
<InputTypes
attributes = {field}
key = {i}
value = {this.state.formData[i]}
obj = {this.state.formData}
errorStatus = {this.state.fieldErrorStatus}
displayError = {this.state.submitErrorStatus}
/>
)
})
}
</div>
<div className = "button-container">
<button className = "submit-button" type = "submit">Submit Details</button>
</div>
</form>
</div>
}
Child Component
render(){
const {
attributes : {
id,
name,
dataType,
rules,
} = {},
displayError,
obj,
errorStatus
} = this.props;
return(
<div className="input-container">
<Input
type = {dataType}
id = {id.toString()}
name = {name || ''}
value = {obj[name]}
error={displayError || errorStatus[name] ? false : true} />
</div>
)
}

So I made you some basic components which you can use as a reference. I've implemented the onChange and onBlur method. I also made an easily accessible error message but I didn't create the onSubmit functions as you just have to map the array while comparing for empty inputs.
Here is my code:
Container:
import React, { Component } from 'react';
import Field from './Field';
export default class Container extends Component {
state = {
// Create fields
fields: [
{ key: "0", errorMessage: 'Error message for field: 0', isValid: true },
{ key: "1", errorMessage: 'Error message for field: 1', isValid: true },
{ key: "2", errorMessage: 'Error message for field: 2', isValid: true },
{ key: "3", errorMessage: 'Error message for field: 3', isValid: true },
{ key: "4", errorMessage: 'Error message for field: 4', isValid: true }
]
}
render() {
return this.state.fields.map((field, i) => {
return <Field
key={field.key}
isValid={field.isValid}
onChange={this.onInputChange}
index={i}
/>
});
}
onInputChange = (index, event) => {
let newState = this.state;
if(event.target.value === '') {
// Set field invalid
newState.fields[index].isValid = false;
// In this case log but you could do other stuff with the message
console.log(this.state.fields[index].errorMessage);
} else {
// Set field valid
newState.fields[index].isValid = true;
}
this.setState(newState);
}
}
Input:
import React, { Component } from 'react';
export default class Field extends Component {
render() {
// Get props
const {isValid, onChange, index} = this.props;
return <input
type="text"
// Check the input
onInput={event => onChange(index, event)}
onBlur={event => onChange(index, event)}
// If invalid make the background red
style={{backgroundColor: isValid ? 'white' : 'red'}}
/>
}
}
I hope it helps =)

Related

React Input onSelect Component shows Previous Selection

I've create an autocomplete search input bar using AWS Cloudscape. When I attempt to use the onSelect to save state, it does not save state. When I select a second item from the search bar, the first item then shows up. I assume this is a state async issue. I need the most recent selection, how do I get that?
import React from "react";
import Regions from "./trailforks_regions.json"
import RegionNames from "./region_names.json"
import * as dfd from "danfojs";
import { Autosuggest } from '#cloudscape-design/components';
export class AutoCompleteInputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
df: new dfd.DataFrame(Regions),
value: null,
final_value: "",
options: [],
status: "loading",
regionContext: [],
uri_value: "",
loaded: false
};
}
lookupRegionUriName() {
console.log("Checking for uri region name on:" + this.state.final_value)
let region_name_split = this.state.final_value.split(",")[0]
let query_df = this.state.df.query(this.state.df["name"].eq(region_name_split))
let uri_name = query_df["region_uri_name"].values
console.log("found:" + uri_name)
return uri_name
}
handleChange = event => {
this.setState({ value: event.detail.value });
};
handleSelect = event => {
console.log(event) // This shows the correct data! But the set state does not set this data.
this.setState({ final_value: event.detail.value });
this.lookupRegionUriName(this.state.final_value)
};
handleLoadItems = ({ detail: { filteringText, firstPage, samePage } }) => {
this.filteringText = filteringText;
var my_options = RegionNames.filter(regions => regions.toLowerCase().startsWith(this.filteringText.toLowerCase()))
var region_values = []
for (var i = 0; i <= my_options.length; i++) {
region_values.push({ value: my_options[i] })
}
this.setState({
options: region_values.slice(0, 25),
status: 'loading',
});
};
enteredTextLabel = value => `Use: "${value}"`;
renderInput() {
if (this.state.df != null) {
const { status, value, options, final_value, uri_value } = this.state;
return (
<>
<Autosuggest
onChange={this.handleChange}
onSelect={this.handleSelect}
onLoadItems={this.handleLoadItems}
value={value}
options={options}
enteredTextLabel={this.enteredTextLabel}
ariaLabel="Autosuggest example with suggestions"
placeholder="Enter Trailforks Region Name"
empty="No matches found"
finishedText={this.filteringText ? `End of "${this.filteringText}" results` : 'End of all results'}
//status={status}
loadingText="searching"
filteringType="manual"
/>
</>
)
} else {
return (<></>)
}
}
render() {
return (
<>
{this.renderInput()}
</>
)
}
}
Figured it out. It was a state issue, here are the changes for future people:
import React from "react";
import Regions from "./regions.json"
import RegionNames from "./region_names.json"
import * as dfd from "danfojs";
import { Autosuggest } from '#cloudscape-design/components';
export class AutoCompleteInputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
df: new dfd.DataFrame(Regions),
value: null,
final_value: "",
options: [],
status: "loading",
regionContext: [],
uri_value: "",
loaded: false
};
}
updateRegion(state_object) {
this.lookupRegionUriName()
return state_object.final_value
}
lookupRegionUriName() {
console.log("Checking for uri region name on:" + this.state.final_value)
let region_name_split = this.state.final_value.split(",")[0]
let query_df = this.state.df.query(this.state.df["name"].eq(region_name_split))
let uri_name = query_df["region_uri_name"].values
this.setState({uri_value: uri_name})
console.log("found:" + uri_name)
}
handleChange = event => {
this.setState({ value: event.detail.value });
};
handleSelect = event => {
console.log(event) // This shows the correct data! But the set state does not set this data.
this.setState({ final_value: event.detail.value });
this.lookupRegionUriName(this.state.final_value)
};
handleLoadItems = ({ detail: { filteringText, firstPage, samePage } }) => {
this.filteringText = filteringText;
var my_options = RegionNames.filter(regions => regions.toLowerCase().startsWith(this.filteringText.toLowerCase()))
var region_values = []
for (var i = 0; i <= my_options.length; i++) {
region_values.push({ value: my_options[i] })
}
this.setState({
options: region_values.slice(0, 25),
status: 'loading',
});
};
enteredTextLabel = value => `Use: "${value}"`;
renderInput() {
if (this.state.df != null) {
const { status, value, options, final_value, uri_value } = this.state;
return (
<>
<Autosuggest
onChange={this.handleChange}
onSelect={event => {this.setState({final_value: event.detail.value}, () => {this.updateRegion(this.state)})}}
//onSelect={this.handleSelect}
onLoadItems={this.handleLoadItems}
value={value}
options={options}
enteredTextLabel={this.enteredTextLabel}
ariaLabel="Autosuggest example with suggestions"
placeholder="Enter Trailforks Region Name"
empty="No matches found"
finishedText={this.filteringText ? `End of "${this.filteringText}" results` : 'End of all results'}
status={status}
loadingText="searching"
filteringType="manual"
/>
</>
)
} else {
return (<></>)
}
}
render() {
return (
<>
{this.renderInput()}
</>
)
}
}

Collect checkbox values as an array React

I have a checkbox component, I want my user to be able to check multiple items, and then the items to be saved in the state as an array.
If I select a checkbox my handleChange function seems to set my array to undefined, I'm not sure if it's the way I am sending the data or If I've setup my checkbox wrong, I'm quite new to React.
My main component is
export default class MainForm extends Component {
state = {
eventFormats: []
}
handleChange = input => event => {
this.setState({[input]: event.target.value})
console.log(this.state)
}
render() {
const eventFormat = {eventFormats: this.state.eventFormats}
return <EventFormat
nextStep={this.nextStep}
handleChange={this.handleChange}
values={eventFormat}
}
}
}
My event form component
export default class EventFormat extends Component {
state = {
eventFormats: [
{id: 1, value: 1, label: "Virtual", isChecked: false},
{id: 2, value: 2, label: "Hybrid", isChecked: false},
{id: 3, value: 3, label: "Live", isChecked: false},
]
}
saveAndContinue = (e) => {
e.preventDefault()
}
render() {
return (
<Form>
<h1 className="ui centered">Form</h1>
<Form.Field>
{
this.state.eventFormats.map((format) => {
return (<CheckBox handleChange={this.props.handleChange} {...format} />)
})
}
</Form.Field>
<Button onClick={this.saveAndContinue}>Next</Button>
</Form>
)
}
}
And finally my checkbox component
const CheckBox = (props) => {
return (<Checkbox label={props.label} onChange={props.handleChange('eventFormats')}/>)
}
export default CheckBox
The error is in your handleChange function, which sets state to a dictionary while you said you want the checkbox's value to be added to the eventFormats array in the state.
export default class MainForm extends Component {
state = {
eventFormats: []
}
handleChange = input => event => {
if (event.target.checked) {
this.setState({eventFormats: this.state.eventFormats.concat([event.target.value])});
} else {
const index = this.state.indexOf(event.target.value);
if (index === -1) {
console.error("checkbox was unchecked but had not been registered as checked before");
} else {
this.setState({eventFormats: this.state.eventFormats.splice(index, 1);
}
}
console.log(this.state)
}
render() {
const eventFormat = {eventFormats: this.state.eventFormats}
return <EventFormat
nextStep={this.nextStep}
handleChange={this.handleChange}
values={eventFormat}
}
}
}
There are a few things to fix:
this.setState({[input]: event.target.value})
this will always overwrite the array(eventFormats) with event.target.value.
<CheckBox handleChange={this.props.handleChange} {...format} />
in the above line, you're passing all the properties in each format object
const CheckBox = (props) => {
return (<Checkbox label={props.label} onChange={props.handleChange('eventFormats')}/>)
}
but here you're only using label and handleChange.
Here's a React StackBlitz that implements what you're looking for. I used <input type="checkbox" />, you can replace this with the Checkbox component you want. See the console logs to know how the state looks after toggling any of the checkboxes.
Also, added some comments to help you understand the changes.
const Checkbox = ({ id, checked, label, handleChange }) => {
return (
<>
<input
type="checkbox"
id={id}
value={checked}
// passing the id from here to figure out the checkbox to update
onChange={e => handleChange(e, id)}
/>
<label htmlFor={id}>{label}</label>
</>
);
};
export default class App extends React.Component {
state = {
checkboxes: [
{ id: 1, checked: false, label: "a" },
{ id: 2, checked: false, label: "b" },
{ id: 3, checked: false, label: "c" }
]
};
handleChange = inputsType => (event, inputId) => {
const checked = event.target.checked;
// Functional update is recommended as the new state depends on the old state
this.setState(prevState => {
return {
[inputsType]: prevState[inputsType].map(iT => {
// if the ids match update the 'checked' prop
return inputId === iT.id ? { ...iT, checked } : iT;
})
};
});
};
render() {
console.log(this.state.checkboxes);
return (
<div>
{this.state.checkboxes.map(cb => (
<Checkbox
key={cb.id}
handleChange={this.handleChange("checkboxes")}
{...cb}
/>
))}
</div>
);
}
}

Conditionally disable React Checkbox

I am trying to conditionally disable the checkbox in react, based on the count. Passing the value through props whether it is checked and greater than the number. I am saving the name in the state to further process it to send to in the backend database.
Here is my react code.
class CheckboxComponent extends Component {
constructor(props) {
super(props);
this.state = {
checkedItems: {}
};
}
handleChange = (event, formKey) => {
const {checkedItems} = this.state;
const checkedValues = {...checkedItems};
checkedValues[event.target.name] = event.target.checked;
this.setState((prevState, currState) => {
return {
...prevState,
checkedItems: checkedValues
}
});
};
render = () => {
const {checkedItems} = this.state;
const checkedValues = {...checkedItems};
const checkedCount = Object.values(checkedValues).length;
const checked = Object.values(checkedValues);
const disabled = checkedCount >= 3;
return (
<div>
{checkboxes.map((item, index) => (
<label className={`form__field__input__label`} key={item.key}>
<Input
type={`checkbox`}
name={item.name}
checked={this.state.checkedItems[item.name] || false}
onChange={this.handleChange}
formKey={'subjects'}
disabled={(!checked[index] && checked.length > 3)}
/>
{item.name}
</label>
))}
</div>
)
This is the Array that I am passing to render the values in the checkbox
const checkboxes = [
{
name: "Math and economics",
key: "mathsandeconomics",
label: "Math and economics"
},
{
name: "Science",
key: "Science",
label: "Science"
},
The below code snippet will work fine for you. And you can sent object to the backend having maximum of only 3 properties set to true. Get the full code from codesandbox link https://codesandbox.io/s/emmeiwhite-0i8yh
import React from "react";
const checkboxes = [
{
name: "Math and economics",
key: "mathsandeconomics",
label: "Math and economics",
},
{
name: "Science",
key: "science",
label: "Science",
},
{
name: "history",
key: "history",
label: "history",
},
{
name: "literature",
key: "literature",
label: "literature",
},
];
class CheckboxComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedItems: {},
count: 0,
};
}
handleChange = (event, formKey) => {
const { name, checked } = event.target;
const updatedCheckedItems = { ...this.state.checkedItems, [name]: checked };
this.setState({
checkedItems: updatedCheckedItems,
count: Object.values(updatedCheckedItems).filter((value) => value).length,
});
};
render = () => {
const checkedValues = { ...this.state.checkedItems };
const checkedCount = Object.values(checkedValues).filter((value) => value)
.length;
console.log(this.state.checkedItems);
return (
<div>
{checkboxes.map((item, index) => (
<label className={`form__field__input__label`} key={item.key}>
<input
type={`checkbox`}
name={item.name}
checked={this.state.checkedItems[item.name] || false}
onChange={this.handleChange}
disabled={!checkedValues[item.name] && checkedCount > 2}
/>
{item.name}
</label>
))}
</div>
);
};
}
export default CheckboxComponent;
Your checked.length counts all touched boxes, not checked only. If you uncheck an input, it still will be counted. Count only true, for example Object.values(checkedValues).filter(value => value).length.
Use names instead of indexes: disabled={!checkedValues[item.name] && checkedCount > 3}
You can see full solution here: https://codesandbox.io/s/confident-http-vlm04?file=/src/App.js
event.target.getAttribute('name');
try this to get name attribute, pretty sure event.target.name is 'undefined'
I see one use case is not taken care of. checkedCount should count the number of true values only.
const checkedCount = Object.values(checkedValues).length; // existing
const checkedCount = Object.values(checkedValues).filter(item=>item==true).length //replace with this line
This would solve the problem.
Here is the code and as well as codesandbox link
Codesandbox Link
import React from "react";
export class CheckboxComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedItems: {},
checkedCount: 0
};
}
handleChange = (event, formKey) => {
const { checkedItems } = this.state;
const checkedValues = { ...checkedItems };
checkedValues[event.target.name] = event.target.checked;
this.setState((prevState, currState) => {
return {
...prevState,
checkedItems: checkedValues,
checkedCount: event.target.checked
? prevState.checkedCount + 1
: prevState.checkedCount - 1
};
});
};
render = () => {
const { checkboxes } = this.props;
const { checkedCount } = this.state;
const disabled = checkedCount >= 3;
return (
<div>
<p></p>
{checkboxes.map((item, index) => (
<label className={`form__field__input__label`} key={item.key}>
<input
type={`checkbox`}
name={item.name}
checked={this.state.checkedItems[item.name] || false}
onChange={this.handleChange}
disabled={!this.state.checkedItems[item.name] ? disabled : false}
/>
{item.name}
</label>
))}
</div>
);
};
}

How to toggle between one checkbox and a whole group of checkboxes?

My purpouse here is to create a group of checboxes. "Search everywhere" is default checked, if you check something else "Search everywhere" automatically unchecked, you can check as many different checkboxes as you want, until you check "search everywhere" again, if you do that all other checkboxes will unchecked.
I want to create it in function component with Hooks in React.
View: Image how it looks in browser
Everything is ready, but I stuck a little bit with toggle between one checkbox and group of checkboxes. I've tried useState and useEffect to controll useState callback. Thanks for help.
const ButtonCategory = (props) => {
const [state, setState] = useState({
normalCheckbox: false,
specialCheckbox: true
});
const { id, name, special, products } = props;
const toggleOthers = () => {
if (state.specialCheckbox) {
setState({
...state,
normalCheckbox: false // ofc its bad
});
} else if (state.normalCheckbox) {
setState({
...state,
specialCheckbox: false // ofc its bad
});
}
};
const toggleNormal = () => {
setState({
...state,
normalCheckbox: !state.normalCheckbox
});
};
const toggleSpecial = () => {
setState({
...state,
specialCheckbox: !state.specialCheckbox
});
};
useEffect(() => {
toggleOthers();
}, [state.specialCheckbox, state.normalCheckbox]);
return (
<>
<Label>
<StyledInput
type="checkbox"
id={id}
checked={special ? state.specialCheckbox : state.normalCheckbox}
onChange={special ? () => toggleSpecial() : () => toggleNormal()}
onClick={(e) => {
/* do something */
}}
/>{" "}
<div>
{" "}
{name} {special ? null : `(${products})`}
</div>
</Label>
</>
);
};
I believe you want something like this:
import React, { useState } from "react";
export const Checkboxes = () => {
const [checkedIds, setCheckedIds] = useState(new Set(["everywhere"]));
const handleCheck = ({ id, checked }) => {
if (checked) {
if (id === "everywhere") {
checkedIds.clear();
} else {
checkedIds.delete("everywhere");
}
checkedIds.add(id);
} else {
checkedIds.delete(id);
}
setCheckedIds(new Set(checkedIds));
};
return (
<form>
<label>
<input
id="everywhere"
type="checkbox"
checked={checkedIds.has("everywhere")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Search everywhere
</label>
<label>
<input
id="option-1"
type="checkbox"
checked={checkedIds.has("option-1")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Option 1
</label>
<label>
<input
id="option-2"
type="checkbox"
checked={checkedIds.has("option-2")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Option 2
</label>
</form>
);
};
Test case at codesandbox.io
May be this could be helpful
import React from "react";
import "./style.css";
export const App = () => {
const _checkboxes = [
{
id: "id1",
name: "111",
value: "111",
label: "111",
checked: true
},
{
id: "id2",
name: "222",
value: "222",
label: "222",
checked: false
},
{
id: "id3",
name: "333",
value: "333",
label: "333",
checked: false
}
];
const [checkboxes, setCheckboxes] = React.useState(_checkboxes);
const handleChange = id => e => {
setCheckboxes(checkboxes => {
const firstId = "id1";
const temp = checkboxes.map(c => {
if (firstId === id) {
c.checked = c.id === firstId ? !c.checked : false;
} else {
if (c.id === id) {
c.checked = !c.checked;
} else {
if (c.id === firstId) {
c.checked = false;
}
}
}
return c;
});
return [...temp];
});
};
return (
<div>
{checkboxes.map(checkbox => (
<div key={checkbox.id}>
<input
type="checkbox"
onChange={handleChange(checkbox.id)}
value={checkbox.value}
name={checkbox.name}
id={checkbox.id}
checked={checkbox.checked}
/>
<label htmlFor={checkbox.id}>{checkbox.label}</label>
</div>
))}
</div>
);
};
https://stackblitz.com/edit/react-rtxxfp?file=src%2FApp.js

Rendering multiple times?

I have a form component, and the reference of input fields are linked to the useForm reducer with references. I have to set a initial form state after setting the input field references? I have done as below. But it is rendering thrice. How to solve this rendering issue?
import React, { useState } from 'react';
const useForm = () => {
const [ formState, setFormState ] = useState({});
const refs = useRef({});
const register = useCallback(( fieldArgs ) => ref => {
if(fieldArgs) {
const { name, validations, initialValue } = fieldArgs;
refs.current[name] = ref;
}
console.log('Register rendered');
}, []);
useEffect(() => {
console.log('Effect Rendered');
const refsKeys = Object.keys(refs.current);
refsKeys.forEach(refKey => {
if(!formState[refKey]) {
setFormState(prevState => {
return {
...prevState,
[refKey]: {
value: '',
touched: false,
untouched: true,
pristine: true,
dirty: false
}
}
});
}
});
}, [ refs ]);
return [ register ];
}
export { useForm };
And the app component as below
const App = () => {
const [ register ] = useFormsio();
return(
<form>
<input
type = 'email'
placeholder = 'Enter your email'
name = 'userEmail'
ref = { register({ name: 'userEmail' }) } />
<button
type = 'submit'>
Submit
</button>
</form>
)
}
How to solve this multiple rendering issue?
I think the issue in the code above is whenever refs changes you need to loop through all the fields in form and set the state.
Why don't you set the state in register method?
const register = useCallback(( fieldArgs ) => ref => {
if(fieldArgs) {
const { name, validations, initialValue } = fieldArgs;
if(!refs.current[name] ) {
refs.current[name] = ref;
setFormState(prevState => {
return {
...prevState,
[refKey]: {
value: '',
touched: false,
untouched: true,
pristine: true,
dirty: false
}
}
});
}
}
console.log('Register rendered');
}, []);

Categories