I'm creating a form that can get time value of time picker.
But due to the e is already the Moment, I cant get the input name in handleChange.
Is there a way to get it?
Component:
<DateTimeRangePicker
selected={time}
onChange={handleChange}
type='timepicker'
readOnly={false}
texts={{
name: 'time',
placeholder: 'Enter your time out'
}}
timeFormat='HH:MM:SS'
/>
the texts?.name will be a props and will be inside of innerProps of DatetimepickerProps
CustomForm handleChange
const [values, setValues] = useState(initialState)
const [data, setData] = useState(initialState)
const handleChange = useCallback(
(e: any) => {
let result: any
setValues({ ...values, [e.target.name]: e })
if (e._isValid === true) {
result = {
value: e._d,
status: true
}
} else {
result = {
value: e._d,
status: false
}
}
setData({ ...data, [e.target.name]: result })
},
[data]
)
It has to be e.target.name, since the CustomForm onSubmit will get the other input component throught its e.target.name
For the component DateTimeRangePicker from react-datetime-picker
What you receive from the picker onChange, is not the typical event that you would get from a Jsx element; rather it is the time value of the picker
You can see it in the source code via this link
A solution would be to wrap your handleChange and use a constant to define the name of your property as follow
const TIME_NAME = "time";
const YourComponent = () => {
const [values, setValues] = useState(initialState)
const [data, setData] = useState(initialState)
const handleChange = useCallback(
(e: any) => {
let result: any
setValues({ ...values, [e.target.name]: e.target.value })
if (e._isValid === true) {
result = {
value: e.target.value,
status: true
}
} else {
result = {
value: e.target.value,
status: false
}
}
setData({ ...data, [e.target.name]: result })
},
[data]
);
const handleDateTimeRangePickerChange = (_value) => {
handleChange({target: {name: TIME_NAME, value: _value }});
}
return <DateTimeRangePicker
name={TIME_NAME}
selected={time}
onChange={handleDateTimeRangePickerChange}
type='timepicker'
readOnly={false}
texts={{
name: 'time',
placeholder: 'Enter your time out'
}}
timeFormat='HH:MM:SS'
/>
}
Related
I have a state of FormData
const [FormData, setFormData] = useState({}) as any;
I have an object cir which contains many keys.
How to set the FormData with cir initially and later update with form?
Object.keys(cir).forEach((key) => {
setFormData({ ...FormData, [key]: cir.key });
console.log(key, cir[key]);
});
const [FormData, setFormData] = useState({}) as any;
const [Test, setTest] = useState<object>();
// States-------States-------States-------States-------States-------States-------States-------States-------
// This is Working
const onChange = (e: any) => {
const { name, value } = e.target;
setFormData({ ...FormData, [name]: value });
};
// useEffect----------useEffect----------useEffect----------useEffect----------useEffect----------useEffect
useEffect(() => {
if (!user) {
navigate("/");
}
dispatch(getCIR(params._id));
console.log(FormData);
}, []);
// useEffect----------useEffect----------useEffect----------useEffect----------useEffect----------useEffect
const setInitialFormData = () => {
Object.keys(cir).forEach((key : string) => {
// This is not working. Why?????
setFormData({ ...FormData, key: cir[key] });
});
}
if (isSuccess) {
setInitialFormData();
}
This is not working.
I'm trying to make a custom reusable useForm hook with inputs validation. I want form to be validated on Submit and clear validation when user typing something in the field. Actually the problem with validation. I do not understand how to implement it.
Hook accepts inital state for inputs which is an object with keys and values as empty strings:
{
"name": "",
"description": ""
}
Hooks also accept a callback function which is actually a submitHandler which fires on submit click but do nothing at the moment.
I have a submit function in useForm hook which expected to fire when a form is valid. To check the form validity I loop through inputs calling validator function on each input. If input is not valid I set an error for that field.
But its not clear what to do next. If I console.log(error) in submit function right after forEach loop after first submit button click its an empty object (on second button click it has errors but not for the first click).
May I ask for help tp deal with problem? How to make this validation work correctly?
Submit function in useForm hook:
const submit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
Object.entries(input).forEach(([field, value]) => {
validator(field, value)
})
console.log(error) // it's an empty object on first submit button click
callback();
};
Validator function:
const validator = (field: keyof Error, value: Input['key']) => {
switch (field) {
case 'name':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for name field',
}));
}
break;
case 'description':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for description field',
}));
}
break;
default:
break;
}
};
Full hook code:
import { useState } from 'react';
type Input = {
[key: string]: string;
};
type Error = {
[key: string]: string | undefined;
};
const useForm = (initValue: Input, callback: () => void) => {
const [input, setInput] = useState(initValue);
const [errors, setErrors] = useState<Error>({});
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
clearValidation(name);
setInput((prevState) => ({
...prevState,
[name]: value,
}));
};
const resetForm = () => {
setInput({});
};
const submit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
Object.entries(input).forEach(([field, value]) => {
validator(field, value);
});
callback();
};
const validator = (field: keyof Error, value: Input['key']) => {
switch (field) {
case 'name':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for name field',
}));
}
break;
case 'description':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for description field',
}));
}
break;
default:
break;
}
};
const clearValidation = (field: keyof Error) => {
if (field) {
setErrors((prevState) => ({
...prevState,
[field]: undefined,
}));
return;
}
setErrors({});
};
return { input, errors, setInput, handleChange, resetForm, submit };
};
export default useForm;
The reason the console.log is empty is because react state setting is asynchronous. It's probably setting it fine, it just happens shortly after the console.log because of how React batches state updates and runs them later seamlessly.
I would suggest logging it in the render method or something.
See here for more details: https://beta.reactjs.org/apis/usestate#ive-updated-the-state-but-logging-gives-me-the-old-value
export const Modal = (props, { farm, id }) => {
const [currentUser, setCurrentUser] = useState();
console.log("NewId", props.id);
const initialFieldValues = {
title: "",
image: "",
product: "",
content: "",
phone: "",
email: "",
};
const [values, setValues] = useState(initialFieldValues);
function handleChange(e) {
const { name, value } = e.target;
setValues({
...values,
[name]: value,
});
}
const handleFormSubmit = (e) => {
e.preventDefault();
const obj = {
...values,
};
getFirebase()
.database()
.ref(`Users/${props.id}`)
// .ref(`Users/${newId}`)
.push(obj, (err) => {
if (err) console.log(err);
// console.log("ID", newId);
});
};
PARENT COMPONENT
const App = (props) => {
const [currentUser, setCurrentUser] = useState();
const [id, setId] = useState("");
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.on("value", (snapShot) => {
setCurrentUser({ key: snapShot.key, ...snapShot.val() });
});
// }
}
if (setCurrentUser(userAuth)) {
} else if (!userAuth && typeof window !== "undefined") {
return null;
}
const id = userAuth.uid;
setId(id);
});
<>
<Modal id={id} />
</>;
return unsubscribe;
}, []);
When i console.log it here .ref(Users/${props.id}) it's undefined.
This is how my console looks like:
12modal.jsx:12 'NewId' fRHyyzzEotPpIGUkkqJkKQnrTOF2
12modal.jsx:12 'NewId' undefined
it comes one time with the ID and 12 times undifiend.
Any advice would be greatly appreciated.
Firstly you can't change a props values, and if props.id is a state(i.e. const [id, setId] = React.useState(initial value)) then you can change id only by setId which is not received by child, so i think parent might be causing this issue.
These are my states using hooks:
const [adminProfile, setProfile] = useState({
locations: [],
});
const [location, setLocation] = useState({
locationName: "",
location: {},
locationPhone: "",
locationEmail: "",
staff: [],
multipleKitchens: false,
kitchens: [],
});
const [locationList, setLocationList] = useState([]);
const [locationAddress, setAddress] = useState({
streetAddress: "",
streetAddress2: "",
city: "",
state: "",
zip: "",
country: "USA"
});
I have a bunch of fields with onChange handlers and an onClick handler that needs to update 3 states in order. First, LocationAddress has to become the state of the location property within the location state. Second, the location state has to be updated with a unique ID, and then that unique ID is inserted into the array in the locationList state. Finally, the entire array from locationList state is added to the locations property of adminProfile state. These are all in one component.
const handleClickLocation = () => {
setLocation(prevValue => ({
...prevValue,
locationID: uuidv4(),
location: locationAddress
}));
setLocationList(prevValue => [...prevValue, location.locationID]);
setProfile(prevValue => ({
...prevValue,
locations: locationList
}))
The first time the click handler is triggered, it sets only the first state in the handler and sends "undefined" into the second state. When the click handler is clicked a second time, it then behaves normally. I want all the states to update simultaneously. I've tried forceUpdate(), but couldn't figure out the syntax. I've tried using ReactDOM.unstable_batchedUpdates but it still behaved the same.
How can I get this to work? I want to keep this within one component. Is that possible?
Here is the entire code updated with the useEffect hook:
import React, {useState, useEffect} from "react";
import axios from "axios";
const { v4: uuidv4 } = require('uuid');
const CompanyProfileInfo = (props) => {
const todayDate = () => {
let today = new Date();
let day = today.getDate();
let month = today.getMonth() + 1;
let year = today.getFullYear();
if (day < 10) day = '0' + day;
if(month < 10) month = '0' + month;
return (month + "/" + day + "/" + year);
}
const [adminProfile, setProfile] = useState({
companyID: props.companyInfo.companyID,
firstName: "",
lastName: "",
phonePrimary: "",
phoneSecondary: "",
emailSecondary: "",
streetAddress: "",
streetAddress2: "",
city: "",
state: "",
zip: "",
country: "USA",
multipleLocations: false,
locations: [],
membershipLevel: "Basic",
joinedDate: todayDate(),
});
const [location, setLocation] = useState({
locationName: "",
locationPhone: "",
locationEmail: "",
staff: [],
multipleKitchens: false,
kitchens: [],
});
const [locationAddress, setAddress] = useState({
streetAddress: "",
streetAddress2: "",
city: "",
state: "",
zip: "",
country: "USA"
});
const [locationList, setLocationList] = useState([]);
useEffect(() => {
setLocationList(prevValue => [...prevValue, location.locationID]);
}, [location.locationID]);
useEffect(() => {
if (locationList[0] === undefined) {
{locationList.shift()}
}
setProfile(prevValue => ({
...prevValue,
locations: locationList
})
)
}, [locationList])
const handleChange = (event) => {
const {name, value} = event.target;
setProfile(prevValue => ({
...prevValue,
[name]: value
}))
}
const handleChangeLocations = (event) => {
const {name, value} = event.target;
setLocation(prevValue => ({
...prevValue,
[name]: value
}));
};
const handleChangeLocations1 = (event) => {
const {name, value} = event.target;
setAddress(prevValue => ({
...prevValue,
[name]: value
}));
};
const handleClickLocation = () => {
setLocation(prevValue => ({
...prevValue,
locationID: uuidv4(),
location: locationAddress,
}));
};
const handleClick = () => {
axios.post('http://localhost:3001/profileinfo', adminProfile)
.then(res => {
props.supportFunctions.setUpLocations(res);
})
.catch(function (error) {
console.log(error);
})
}
return (
<div>
</div>
)
}
export default CompanyProfileInfo;
setState is asynchronous, it means that when it is called, its state won't update at the same time, it takes some time to perform its action.
You can make use of useEffect to do that.
useEffect will perform an action only when the specified state (inside brackets) changes
useEffect(() => {
setLocation({
...location,
location: locationAddress,
locationID: uuidv4()
})
}, [locationAddress]) //When locationAddress changes, setLocation
useEffect(() => {
setLocationList([
...locationList,
location.locationID
])
}, [location]) //When location changes, insert Id
Ps: You can have multiple useEffects in your code.
Updating of the state is asynchronous behavior, because of that you are getting locationID undefined for setLocationList.
Inside class component, we can use a callback to setState call like this -
this.setState({ data: newData }, () => { console.log("This will get printed after setState") })
But in your case, you are using function component so you have to use useEffect react hook to listen for changes in your data and then update other data in the state.
Take a look at this question - How to use `setState` callback on react hooks
constructor(props) {
super(props)
this.state = {
isEdit: false,
currentProduct : {
sku: '',
productName: '',
description: '',
duration: '',
},
}
}
handleChange = (e) => {
this.setState({
currentProduct: {
...this.state.currentProduct,
[e.target.name]: e.target.value
}
})
}
clickHandle = (e) => {
e.preventDefault()
const currentProduct = {...this.state.currentProduct}
currentProduct.id = this.props.match.params.id
this.props.updateProduct(currentProduct)
this.props.history.push('/')
}
When updating field it updates the values but when i goes again to update single value it update only that and removes the other don't know why
handleChange = (e) => {
this.setState({
...this.state.currentProduct,
[e.target.name]: e.target.value
})
}
you are not destructuring entire state first. so do ...state. otherwise isEdit field will be lost.
handleChange = e => {
this.setState({
...this.state,
currentProduct: {
...this.state.currentProduct,
[e.target.name]: e.target.value
}
});
};