I have a problem with react. with a form, I created more inputs that are controlled. I created a function that runs when the form changes. which but updates a state. but reacte gives me an error.
VM2529 react_devtools_backend.js:4026 Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
handleChange func:
let DefaultValue = {
firsName: '',
lastname: '',
email: '',
message: ''
}
const [output, setOutput] = useState(DefaultValue)
const handleChange = (e) => {
setOutput({ [e.target.name]: e.target.value });
}
thank you for your reply.
setOutput({ [e.target.name]: e.target.value });
This will not be merged with the old state, it will replace the old state. So if they change lastName, the new state will be { lastName: "some string" }, and all other values will be undefined.
Instead, you need to copy all the other values from the old state:
setOutput(prev => ({
...prev,
[e.target.name]: e.target.value
});
Related
I implemented a ChipInput component which when focused will behave a string and onBlur will be transformed into a Chip component with the chips introduced by a comma. All works well except for a single issue. When I type in multiple words, and I want to add a new letter in between, the cursor takes me to the end but I want the cursor to stay at the same position.
Could someone please help me out? Thanks.
This is my Stackblitz link
This issue occurs because the ChipInput component is rerendered every time there is a change in the value of name in index.tsx.
One solution to this problem is to change the way state inside ChipInput.tsx is updated.
Firstly, remove the following useEffect code from ChipInput.tsx:
useEffect(() => {
setState({
inputValue: values,
value: compact(map(split(values, separator), (i) => trim(i))),
});
}, [values]);
Change the initial value of state to:
const [state, setState] = useSetState<IChipInputState>({
value: compact(map(split(values, separator), (i) => trim(i))),
inputValue: values,
isFocused: false,
});
Modify the setState statement the deleteChip function to:
setState({
value: newChipValues,
inputValue: join(newChipValues, `${separator} `),
isFocused: false,
});
The deleteChip function would then look like:
const deleteChip = (chipValue: string): void => {
const newChipValues = without(state.value, chipValue);
setState({
value: newChipValues,
inputValue: join(newChipValues, `${separator} `),
isFocused: false,
});
onHandleChange(join(newChipValues, `${separator} `), name);
};
Finally add
setState(prev => ({...prev, inputValue: onChangeValue }));
to the end of the onInputChange prop of the Autocomplete component in ChipInput.tsx. The onInputChange prop would then look like:
onInputChange={(event, newInputValue) => {
let onChangeValue = newInputValue;
if (acceptOnlyNumbers) {
onChangeValue = replace(newInputValue, /[^\d, ]/, '');
}
if (acceptOnlyNumbersPercentage) {
onChangeValue = replace(newInputValue, /[^\d,% ]/, '');
}
onHandleChange(onChangeValue, name);
setState(prev => ({...prev, inputValue: onChangeValue }));
}}
I am making a form to update addresses. When the user clicks on the submit button, the form isn't being submitted but after the click the errors are updated correctly and the form submits fine. Here is what im working with
const [errors, setErrors] = React.useState({
firstName: '',
lastName: '',
streetAddress: '',
streetAddress2: '',
city: '',
zipCode: ''
});
const validateRequiredFields = () => {
let inputError = {};
Object.keys(user).forEach(
(key) => {
inputError = { ...inputError, ...RequiredValidation(user, key) };
}
);
setErrors({ ...errors, ...inputError });
The Validation returns inputError which takes in the values and sets to null instead of empty strings if theres no error. When I console.log this it returns the correct format. Also if there is errors, the errors are updated correctly on first click. Having looked into this I saw that most recent state isn't always fetched which I believe is my problem & the other threads I looked into recommended something like this although I don't know if thats how I am supposed to set it up.
setErrors({ ...errors, ...inputError }, () => {
setErrors({ ...errors, ...inputError });
});
Use
setErrors((prevErrors) => ({ ...prevErrors, ...inputError }));
I have this code to validate a password and email:
const [register, setRegister] = useState({
email: '',
password: '',
error: '',
validation: []
})
function validate (e) {
let valid = true
setRegister((register) => ({ ...register, validation: [] }))
if (register.email === '') {
setRegister((register) => ({ ...register, validation: register.validation.concat('E-mail is required') }))
valid = false
}
if (register.password === '') {
setRegister((register) => ({ ...register, validation: register.validation.concat('Password is required') }))
valid = false
}
return valid
}
Why do I need to use
setRegister((register) => ({ ...register, validation: register.validation.concat('E-mail is required') }))
instead of
setRegister({ ...register, validation: register.validation.concat('E-mail is required') })
to add both email and password validation in array validation?
setRegister is the asynchronous method, so you can't get the updated value of the register after immediately setRegister.
So, instead passing the param directly to the setRegister, you need to pass the callback param with the prevstate.
Let's assume both register.email and register.password are empty so we should setRegister for email and password required validation.
After the first setRegister with just params, register won't be updated immediately, so second setRegister will be based on the initial register value, not the email required validation register value.
If we use callback with prev state, it is not based on the register value at the time of the calling setRegister, but the previous register state. Thus finally register will be updated to the both email and password required one.
Refer to Hooks API Reference
const [state, setState] = useState({ city: '', country: '' });
const handleCityChange = event => {
setState(prevState => {
console.log(prevState);
return { ...prevState, city: event.target.value };
});
};
//...
<input
type="text"
placeholder="city"
value={state.city}
onChange={handleCityChange}
/>
I'm trying to destructuring prevState, which is an object, and update only the city property. On first keystroke, it's working fine without error, but as soon as I'm typing second letter, i will hit error
Uncaught TypeError: Cannot read property 'value' of null
May I know which part is the null coming from? And from chrome console I see below warning not sure if it's related
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property target on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See https:...//react-event-pooling for more information.
UPDATES:
If we're not using setState functionally, then it's working fine?
const handleCityChange = event => {
setState({ ...state, city: event.target.value });
};
An event has to be handled synchronously in React. You can extract the value from the target before you call setState:
const handleCityChange = event => {
const { value } = event.target;
setState(prevState => {
return { ...prevState, city: value };
});
};
Another way of going about it is to persist the event, and it can be used asynchronously.
const handleCityChange = event => {
event.persist();
setState(prevState => {
return { ...prevState, city: event.target.value };
});
};
I have the following component that is submitting new posts. The form is using materiail-ui. I can successfully change the state. But when I submit the form, it gave me the error such that {state is empty} error. But on render event, I tried to console log the state, and I can see the state is changing whenever I type in the new values. Below is the sample of code. For the full codes you can check in my github , which is https://github.com/HtunHtunHtet/reactProject2/blob/master/client/src/components/add_post.js. Thanks in advance.
class AddPost extends Component {
state = {
postCategory: "react",
postTitle: "",
postAuthor: "",
postContent: ""
};
handleChange = (event, index, postCategory) => this.setState({postCategory});
handleSubmit(e){
e.preventDefault();
console.log(this.state);
const data = {
timestamp: Date.now(),
title: this.state.postTitle,
body: this.state.postContent,
author: this.state.postAuthor,
category: this.state.postCategory,
deleted: false,
voteScore: 1
};
this.props.fetchAddPost(data);
this.props.history.push("/");
}
handleInputChange = e => {
const target = e.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
Make handleSubmit a arrow function Or bind it to the component’s “this” in the constructor. Doing either of this will make sure that “this” refers to your Component, when you use “this” in handleSubmit.