onBlur function - setState incorrect - reactjs

I am trying to run an unit test for the following code. I already test onClick and onChange and this.props
onBlur seems not to be working correctly based on the setState
Here is the method that onBlur is calling
handleChange = (e) => {
let localState = Object.assign({}, this.state)
localState[e.target.name] = e.target.value
let settingsObj = {
0: {
matchValue: e.target.name,
value: e.target.value,
transformType: 'replaceElement'
}
}
if(e.target.name === 'viewtitle'){
this.props.updateViewName(e.target.value);
}
this.props.updateViewXMLValue(settingsObj)
this.setState(localState)
}
Here is the onBlur event:
Title
</div>
<div className='tab-input-container'>
<input name='viewtitle' type='text' className="shape-dropdown" placeholder='Title' defaultValue={this.props.defaultData.viewtitle[0].length === 0 ? null : this.props.defaultData.viewtitle[0]} onBlur={this.handleChange}/>
</div>
Here is my test file:
it('Testing onBlur event on ViewTitle', () => {
baseProps.updateViewName.mockClear();
wrapper.setProps({
defaultData:{
viewtitle:[ [] ],
}
});
wrapper.setState({
localState: "blur"
});
wrapper.update()
wrapper.find('input[name="viewtitle"]').simulate('blur',
{
target: {
value:'blur-Test',
name:'viewtitle'
}
});
expect(baseProps.updateViewName).toHaveBeenCalled();
expect(wrapper.state('localState')).toEqual('blur-Test');
});
It seems that target value does not interfere

According to your code: localState[e.target.name] = e.target.value the onBlur handler sets the state objects viewtitle key to whatever the input is.
So if your original state was {localState: "blur"}, the new state would be {localState: "blur", viewtitle: "blur-test" }, which your test will fail as it is looking at the localState key.

Related

React setState not setting state of input field

I am using Gatsby. This is my code:
import React from "react"
class ContactCard extends React.Component {
constructor(props) {
super(props);
this.state = { form: { name: "test" }, message: ""};
this.handleChange = this.handleChange.bind(this);
}
handleSubmit = e => {
e.preventDefault();
};
handleChange = e => {
console.log("handleChange: " + e.target.name + " = " + e.target.value);
this.setState({ [e.target.name]: e.target.value, message: "event value: " + e.target.value });
/*
I also tried the following:
this.setState({ name: e.target.value, message: "event value: " + e.target.value });
*/
}
render() {
const { form: { name }, message } = this.state;
return (
<>
<p>{message} name: {name} </p>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={name}
name="name"
onChange={this.handleChange}
/>
</form>
</>
);
}
}
export default ContactCard
When I type in the input box I see the following in the console log:
handleChange: name = testg
And the P tag changes to have the following text:
event value: testg name: test
The input box's value does not change it remains the same no matter what I do. As commented in my code, I also tried to setState on name directly instead of using the event name, that did not work. I also tried the following:
handleChange = e => {
console.log("handleChange: " + e.target.name + " = " + e.target.value);
var newState = { [e.target.name]: e.target.value, message: "event value: " + e.target.value };
this.setState(newState);
}
This results in the same behavior. What am I missing?
Issue
You are incorrectly setting state. Your state shape has name as a property of state.form, so the setState needs to match this shape.
Solution
Nest [e.target.name]: e.target.value in the form object. Ensure you shallow copy any existing form state as well. The React setState will shallow merge the root object, but it won't do a deep merge of nested state updates.
Since react state updates are asynchronous and (currently in react 16.x) react synthetic events are typically quickly nullified and returned to the event pool, you may also want to save the event name and value properties before enqueueing the state update.
handleChange = (e) => {
const { name, value } = e.target;
this.setState((prevState) => ({
form: {
...prevState.form,
[name]: value
},
message: "event value: " + value
}));
};
So you are updating the new state incorrectly.
You should be nesting name within the form property.
Sending from a mobile device, Pardon my formatting.
Can you try binding the value of input to this.state.form.name instead like so:
<input
type="text"
value={this.state.form.name}
name="name"
onChange={this.handleChange}
/>

Jest: How to update a value of a mock function in an onChange method?

In the following code everything works except for the update of the value in the onChange method.
The expected way it should work is:
initial value is an empty string (✓ works)
when a change is made the value should be the value of the change (✗ does not work)
const mockSetFieldValue = jest.fn(() => '');
beforeAll(async () => {
field = {
name: 'password',
value: mockSetFieldValue(),
// ^^^ initial value is picked up, but not the change in onChange
onChange: (e) => {
console.log(e.target.value) // returns: foo123
mockSetFieldValue.mockReturnValue(() => e.target.value);
// ^^^ this does not update the value
},
};
tree = (
<>
<label htmlFor="password">Password</label>
<MyField field={field} />
</>
);
});
it('input accepts a value', () => {
const { getByLabelText } = render(tree);
const input = getByLabelText(/Password/i);
fireEvent.change(input, { target: { value: 'foo123' } });
expect(input.value).toBe('foo123');
});
How would it be possible to update the onChange method to change the value that is set in my component?
I've tried mockImplementationOnce and mockReturnValue. But they don't seem to work that way.
field.value is set once before all your tests using the current value of the mockSetFieldValue function, which returns the empty string. Changing the mockSetFieldValue function therefore has no effect.
You need to be able to set the field.value in onChange, which you could do like this:
onChange: (e) => {
field.value = e.target.value;
},
It is probably better to mock out the whole onChange function:
const mockOnChange= jest.fn();
beforeAll(async () => {
field = {
name: 'password',
value: '',
onChange: mockOnChange,
};
//...
});
Then in the test:
mockOnChange.mockImplementationOnce((e) => { field.value = e.target.value });
fireEvent.change(input, { target: { value: 'foo123' } });

The cursor backspace doesn’t remove last char of number format in react

I am new to react.
For e.g, input value entered 1,2,3,4 and after the event onChange it takes only numbers, then I can
remove 4,3,2 with backspace but not 1. And in HTML DOM also, the 1 cannot be removed.
class House extends Component {
state = {
room: null,
};
componentDidMount() {
if (this.props.house.rent) {
this.setState({ rent: this.props.house.rent });
}
}
onChange = (field, value, mutate) => {
if (field === "houseroom") {
value = parseInt(value.replace(/[#,]/g, ""));
}
mutate({
variables: {
},
});
this.setState({
[field]: value,
});
};
render(){
const {house} = this.props;
<SomeInput
type="text"
value={
(house.room&&
`$${house.room.toLocaleString("en")}`) ||
""
}
onChange={e => {
e.target.placeholder = "Room";
this.onChange("houseroom", e.target.value, mutate);
}}
}
/>
}
It look like a lot of a problem during the update of the state probably due to overrendering elsewhere in the component try to use prevState instead do ensure that state updating can not conflict.
this.setState(prevState => {
[field]:value;
});
Keep me in touch with the result.
Hope this helps someone !
Need to mention "this.state.room" in the input Value and check the prev state using the componentDidUpdate then "this.setState" here and also finally "this.setState" during the event change. Thanks all

Simple setState on object using hooks

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 };
});
};

Why is my input not updating? - React

I can't figure out why my input is not updating. Here is my code:
state = {
org: {
orgName: ''
}
};
updateInput = field => event => {
this.setState({
[field]: event.target.value
})
}
render() {
let { org } = this.state
return (
<input
value={org.orgName}
onChange={this.updateInput('orgName')}
/>
)
}
I type data into the input. It calls updateInput and sets the state. When render is called, the org.orgNameis '' again. This should be working.
I have even added a log in the setState callback:
this.setState({
[field]: event.target.value
}, () => console.log(this.state.org))
and it logs out the org info that has been entered into the input
What am I missing? How do I make this work?
You have a nested object in your state - you are updating this.state.orgName instead of this.state.org.orgName
updateInput = field => event => {
this.setState({
[field]: event.target.value
})
}
needs to be
updateInput = field => event => {
this.setState({
org: {
...this.state.org,
[field]: event.target.value
}
})
}
Would recommend you avoid nesting objects in state though going forward. Will prove difficult to optimize later on.

Resources