Why state show undefined? - reactjs

I am working on form validation and I make two arrays one for check value and the second was check validation but when the page refresh and render then in my console log show undefined in my name state. whether I give him to a true value in the state. Does anyone have any idea why is this?
var validation = [{
name: true,
company: true,
email: true,
phone: true,
message: true,
}]
const [isValidate, setisValidate] = React.useState({ ...validation })
const [error, seterror] = React.useState({
name: '',
company: '',
email: '',
phone: '',
message: '',
});
const isValidFun = (param) => {
let isTrue = true;
if((param === "name" || param === "all") && (!error.name || error.name.length < 5)) {
isValidate.name = false;
isTrue = false;
} else {
isValidate.name = true;
}
setisValidate({ ...isValidate })
return isTrue;
}
const handleChange = (e) => {
error[e.target.name] = e.target.value;
seterror({ ...error });
isValidFun(e.target.name);
};
console.log(isValidate.name)
const sentMail = () => {
let isValid = isValidFun("all");
if (isValid) {
alert('Form submitted')
}
};
return (
<Input
type="text"
name="name"
placeholder="Name"
value={error.name}
onChange={handleChange}
/>
{!isValidate.name && <p className="error">This field is required</p>}
);
};

Your validation object is an array. When you initialise the state, you use the spread operator on the array inside the object, so you end up with an object with key 0 and value of the object.
var validation = [{
name: true,
company: true,
email: true,
phone: true,
message: true,
}]
console.log({...validation}]
Instead, change the validation to an object and assign it to state.
var validation = {
name: true,
company: true,
email: true,
phone: true,
message: true,
}
const [isValidate, setisValidate] = React.useState(validation);

Should be the spreader. validation variable should be like this
var validation = {
name: true,
company: true,
email: true,
phone: true,
message: true,
}

Related

React-typescript, looping over the state in reducer function

How can I loop over the whole useReducer state and set the its "isWrong" property to true if there is no value after form submission. What is the best approach to solve that. Because the problem is that I could loop over the state and make the separate variable but the problem is I need the "isWrong" property set inside state because it makes fields color red when it is wrong.
state = const initialStateReducer: inputsFormState = {
title: {
val: "",
isValid: false,
isClicked: false,
isWrong: false,
},
description: {
val: "",
isValid: false,
isClicked: false,
isWrong: false,
},}
reducer:
const inputReducer = (
state: inputsFormState,
action: inputsFormAction
): inputsFormState => {
let isValid = false;
let isClicked = true;
let isWrong = false;
const { content } = action;
const validateInput = (content: string) => {
isClicked = true;
isValid = content.length > 0;
isWrong = isClicked && !isValid;
};
if (
action.type === ActionKind.stringVal &&
action.field &&
typeof content === "string"
) {
validateInput(content);
return {
...state,
[action.field]: {
val: content,
isValid: isValid,
isClicked: isClicked,
isWrong: isWrong,
},
};
}
Form submission
const onSubmitHandler = (e: React.FormEvent): void => {
e.preventDefault();
if (formIsValid && user?.displayName) {
const recipe: Recipe = {
username: user.displayName,
title: inputsValues.description.val,
type: type,
description: inputsValues.title.val,
id: Math.random(),
time: time,
ingredients: ingredients,
stars: Math.floor(Math.random() * 6) + 1,
steps: steps,
comments: [],
};
dispatch(recipeAction.addRecipe(recipe));
dispatch(sendData(recipe));
navigate("/");
}
};
You can change your validateInput arrow function to augment the state as follows:
const validateInput = (state: inputsFormState, content: string): inputsFormState => {
Object.keys(state).forEach((stateKey) => {
validatedState[stateKey] = {
isClicked: true,
isValid: content.length > 0,
isWrong: isClicked && !isValid,
val: state[stateKey].val,
};
});
};
Then you can use it as follows:
validateInput(state, content);
return { ...state };
Also, if you want want content to be set in all of the val properties, you can change val: state[stateKey].val to val: content.

Error select based on selection from 1st select in React Hooks and dinamically inputs

I have two selects and I want to populate the second select base in the selection of the first one in react. When I select a countrie I want a select2 be displayed with its states and the value on the second select be updated with the value chose.
I have the following code,
const MyForm = (props) => {
const COUNTRIES = [
{
displayValue: "Country1",
value: "C1"
},
{
displayValue: "Country2",
value: "C2"
}
]
const STATES = {
"": [ {
displayValue: "",
value: ""
}],
"C1": [{
displayValue: "State 1",
value: "S11"
},
{
displayValue: "State 2",
value: "S12"
}],
"C2": [{
displayValue: "State n1",
value: "C21"
},
{
displayValue: "STate n2",
value: "C22"
}]
}
let inputsForms = {
country: {
elementType: 'select',
elementConfig: {
type: 'select',
placeholder: '',
options: COUNTRIES,
firstOption: "-- Choose Country"
},
value: ''
},
states: {
elementType: 'select',
elementConfig: {
type: 'select',
placeholder: '',
options: [], // I need these options depend on the countrie selected STATES["C1"]
firstOption: "-- Choose States"
},
value: ''
}
}
const [myForm, setmyForm] = useState(inputsForms);
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
const inputChangedHandler = (e, controlName) => {
const countrieValue = controlName ==="country"?e.target.value:"";
const stateOptions = myForm["states"].elementConfig;
stateOptions["options"] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value
})
});
setmyForm(updatedControls);
}
const ElementsArray = [];
for (let key in myForm) {
ElementsArray.push({
id: key,
config: myForm[key]
});
}
let form = (
<form>
{ElementsArray.map(el => (
<Input
key={el.id}
elementType={el.config.elementType}
elementConfig={el.config.elementConfig}
value={el.config.value}
changed={e => inputChangedHandler(e, el.id)}
firstOption={el.config.firstOption}
/>
))}
</form>
);
return(
<div>
{form}
</div>
);
}
export default MyForm;
The options charge on the select2, however when I select an option on the second select, the options dissappear and the value of the select2 is not updated.
Thanks.
As inputChangeHandler is getting called every input change, the data is resetting even there is change in the state. You could check for the contrieValue and set the state data so the data is not reset.
const inputChangedHandler = (e, controlName) => {
const countrieValue = controlName === "country" ? e.target.value : "";
if (countrieValue) {
const stateOptions = myForm["states"].elementConfig;
stateOptions["options"] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value
})
});
setmyForm(updatedControls);
}
}
You need to add a kind of conditional to update the state whenever the state is selected so that it does not affect the original country object.
const inputChangedHandler = (e, controlName) => {
if (countrolName === 'states') {
// your logic here
return;
}
const countrieValue = controlName === 'country' ? e.target.value : '';
const stateOptions = myForm['states'].elementConfig;
stateOptions['options'] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value,
}),
});
setmyForm(updatedControls);
};

Cannot read property 'files' of undefined for sending multiple images

Code
class Add_Give_Item_Form extends Component {
constructor(props) {
super(props);
this.state = {
// #インプット情報用
info: {
name: '',
owner: '',
keyword1: '',
keyword2: '',
keyword3: '',
bland: '',
state: '未使用、新品',
category: '',
images: [],
detail: '',
},
// Validation用
//  urlは必須項目ではないのでValidationには含めない
message: {
name: '',
keyword1: '',
keyword2: '',
keyword3: '',
state: '',
category: '',
detail: '',
},
allCategory: null,
allBland: null,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleImageSelect = this.handleImageSelect(this);
}
////
...
////
handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
const { info, message } = this.state;
this.setState({
info: { ...info, [name]: value },
});
this.setState({
message: { ...message, [name]: this.validator(name, value) },
});
};
handleImageSelect = (e) => {
this.setState({
info: { ...this.state.info, images: [...this.state.info.images, e.target.files] },
});
};
render() {
const { info, message, allCategory, allBland } = this.state;
// setStateが完了するまではnullにする。
if (this.state.allCategory === null || this.state.allBland === null) {
return <CircularProgress />;
} else {
return (
<div>
///////
.....
///////
<label>Images</label>
<input type="file" multiple onChange={this.handleImageSelect} />
What I want to do
I would like to catch each file sent by a user and put into state as this.state.info.images which is an array.
I saw some questions on stackoverflow and then I found some solutions. When I wrote the same code as what I saw, I got an error like below.
cannot read property files of undefined
I should write the same code but I got the error for some reasons.
I may take another way to realize what I want to do, but I want to write readable codes and figure out why it is happening.
I would like you to teach me why this happens and solutions.
Thank you very much.
I just notice I didn't put bind with this.handleImageSelect = this.handleImageSelect(this).
Now it works well.
Thank you very much.

Cannot focus on first error input in Formik

I have the following implementation of a formik component that renders a form,
I am trying to access the first error field so I can focus on it but with no avail, i will show code
const CompanyProfile = () => {
const CompanySchema = Yup.object().shape({
name: Yup.string()
.min(2, 'too short')
.required(ERROR_REQUIRED),
industry: Yup.string().notRequired(),
address: Yup.string().notRequired(),
crn: Yup.number().required(ERROR_REQUIRED),
website: Yup.string()
.notRequired()
.min(2, 'too short'),
employeesNbr: Yup.string().required(ERROR_REQUIRED),
phoneNumber: Yup.string().required(ERROR_REQUIRED),
userRole: Yup.string().required(ERROR_REQUIRED),
personCheck: Yup.boolean().required(ERROR_REQUIRED),
});
const registerCompany = async values => {
try {
const data = values;
delete data.personCheck;
await addCompany(data);
} catch (error) {
console.log(error);
}
};
const successSubmit = values => {
registerCompany(values);
};
const forSubmit = formik => {
console.log('not valid');
const { errors } = formik;
const keys = Object.keys(errors);
console.log(formik);
if (keys.length > 0) {
const selector = `[id="${keys[0]}"]`;
const errorElement = document.getElementsByName(selector);
errorElement.focus();
}
};
const formik = useFormik({
initialTouched: false,
validateOnChange: true,
validateOnBlur: true,
initialValues: {
name: '',
industry: '',
address: '',
crn: '',
website: '',
employeesNbr: '',
phoneNumber: '',
userRole: '',
personCheck: false,
},
validationSchema: CompanySchema,
onSubmit: values => {
successSubmit(values);
},
handleSubmit: formik => {
forSubmit(formik);
},
});
return (
<Skeleton pageTitle={PAGE_TITLE_COMPANY_PROFILE}>
<CompanyProfileForm formik={formik} />
</Skeleton>
);
};
export default connect(CompanyProfile);
I dont know where I am going wrong, I attached the name,value, onchange correctly in the input fields because I am able to extract the values
Thank you

Checkbox value in React and MongoDB

What I am seeking to accomplish is to have an optional checkbox in a form that returns false when unchecked and true when checked (in the DB).
However, whenever I view my submission in the console, things appear to be find - just not showing up in Mongo. I have attempted numerous things after searching all day both frontend and backend schema. Any help or insight would be much appreciated.
export default class CreateworkOrder extends Component {
constructor(props) {
super(props);
this.onChangeEmployee = this.onChangeEmployee.bind(this);
this.onChangeDescription = this.onChangeDescription.bind(this);
this.onChangeDuration = this.onChangeDuration.bind(this);
this.onChangeDate = this.onChangeDate.bind(this);
this.handleCheckClick = this.handleCheckClick.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
employee: '',
description: '',
duration: 0,
date: new Date(),
employees: [],
isComplete: false
}
}
componentDidMount() {
axios.get('http://localhost:5000/employees/')
.then(response => {
if (response.data.length > 0) {
this.setState({
employees: response.data.map(emp => emp.employee),
employee: response.data[0].employee
})
}
})
.catch((error) => {
console.log(error);
})
}
handleCheckClick = () => {
const complete = !this.state.isComplete;
console.log(complete);
this.setState({ complete: !this.state.isComplete});
}
Then submit below:
onSubmit(e) {
e.preventDefault();
const workOrder = {
employee: this.state.employee,
description: this.state.description,
duration: this.state.duration,
date: this.state.date,
isComplete: this.state.isComplete
}
console.log(workOrder);
axios.post('http://localhost:5000/workOrders/add', workOrder)
.then(res => console.log(res.data)).catch(console.error);
//window.location = '/home';
}
portion of the form to optionally select
<div className="form-group">
<label>Only check box if job has been completed </label>
<input name="isComplete" type="checkbox"
defaultChecked={this.state.isComplete}
onChange={this.handleCheckClick}
className="filled-in" id="filled-in-box"/>
</div>
<div className="form-group">
<input type="submit" value="Create WO" className="btn btn-primary" onSubmit={this.onSubmit}/>
</div>
</form>
DB Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const workorderSchema = new Schema({
employee: { type: String, required: true },
description: { type: String, required: true },
duration: { type: Number, required: true },
date: { type: Date, required: true },
isComplete: { type: Boolean, required: false },
},
{
timestamps: true,
});
const WorkOrder = mongoose.model('WorkOrder', workorderSchema);
module.exports = WorkOrder;
but console does show true
You are using the state variable isComplete but setting the state in complete.
this.state = {
employee: '',
description: '',
duration: 0,
date: new Date(),
employees: [],
isComplete: false
}
In handleCheckClick you are doing:
handleCheckClick = () => {
const complete = !this.state.isComplete;
console.log(complete);
this.setState({ complete: !this.state.isComplete}); }
And you are submitting workOrder which is using isComplete, which you didn't change
const workOrder = { employee: this.state.employee, description:
this.state.description, duration: this.state.duration, date:
this.state.date, isComplete: this.state.isComplete }
This should be the reason. So change the handleCheckClick like this:
handleCheckClick = () => {
let complete = !this.state.isComplete;
console.log(complete);
this.setState({ isComplete: complete});
}
Also, I noticed that you are using const keyword and then trying to change its value. const means the value shouldn't change. Use either let or var in future if you want a variable to be mutable

Resources