TypeError when using focus() - reactjs

I have a react component having a input box. I used componentDidUpdate to set the focus to the input box. But my tests are failing with a typeError. Following is my code . Help me to pass the tests. Than you!!
This is my react component
`
public inputBox: any;
componentDidUpdate = () => {
this.inputBox.focus();
}
<div>
<input
ref = {element => this.inputBox = element}
/>
</div>
`
When the component is updated the tests are failed. giving me this error
TypeError: Cannot read property 'focus' of undefined
can any body can tell me whats wrong here?

Based on the official docs:
Note that when the referenced component is unmounted and whenever the
ref changes, the old ref will be called with null as an argument. This
prevents memory leaks in the case that the instance is stored, as in
the second example. Also note that when writing refs with inline
function expressions as in the examples here, React sees a different
function object each time so on every update, ref will be called with
null immediately before it's called with the component instance.
I believe that the ref callback gets passed null when the input is unmounted (that probably happens when your test ends and a different test starts). I think you just need to test for null when you want to focus the element, like in the docs:
<TextInput ref={function(input) {
if (input != null) {
input.focus();
}
}} />

Related

why would this.state not be available in this scenario?

I have a component with the following functions:
constructor(MyProps: Readonly<MyProps>){
super(MyProps);
this.state = {suppliers: [], supplierId:0, supplierName:''};
this.addSupplier.bind(this);
}
addSupplier(){
const {supplierId} = this.state;
alert('supplierId = ' + supplierId);
}
<Button onClick={this.addSupplier}>Add</Button>
State is initialized as expected b/c this.state.supplierId is bound and displayed as expected in an html input in the component on load. The onChange handler within the html input also calls setState to update state.supplierId as expected. However, when the addSupplier() button gets triggered, the following error occurs:
TypeError: Cannot read property 'supplierId' of undefined
So for some reason, state is not available in this context. Any idea why this is?
Please use the arrow function to create the method
addSupplier = () => {}
or use the below syntax in the constructor
this.addSupplier = this.addSupplier.bind(this);
To expand on what Nikhil Goyal posted, the reason you're getting an error is because the context of this where the function is being invoked. Non-arrow functions implicitly bind their context when invoked rather then when they're declared. So, when you press the onClick on the Button component, it's searching for this.state.supplierId on the Button context.

When testing a React component with Enzyme is it better to use simulate or to call the method directly on the instance()?

If you have a component like this
class Editor extends Component {
handleChange() {
// some code
}
render() {
<div>
<input className="Editor" onChange={this.handleChange} />
</div>
}
}
Is it better to test the handle change by simulating the change event with simulate like this:
wrapper.simulate('change', { // })
Or by calling the method directly by using instance:
wrapper.instance().handleChange()
If you are using Shallow Rendering then .simulate just tries to find the right prop and call it. From the Common Gotchas section for .simulate:
Even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the event you give it. For example, .simulate('click') will actually get the onClick prop and call it.
There isn't any advantage to calling .simulate when using Shallow Rendering and simply calling the prop directly avoids issues caused by the event not mapping to the correct prop.
If you are using Full DOM Rendering then .simulate will fire an event that ends up calling runEventsInBatch from the comically named ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events.
So using .simulate with mount will actually simulate the event, just note from the Common Gotchas section that:
ReactWrapper will pass a SyntheticEvent object to the event handler in your code. Keep in mind that if the code you are testing uses properties that are not included in the SyntheticEvent, for instance event.target.value, you will need to provide a mock event...for it to work.
For Full DOM Rendering it is up to you to determine if there is any value in having ReactDOM simulate the event to call your handler or to just call your handler directly.
From this post by an Airbnb dev:
In general, I've found it's best to invoke the prop directly and avoid .simulate.
For your test it would look something like this:
test('onChange', () => {
const wrapper = shallow(<Editor />); // works with shallow or mount
const onChangeHandler = wrapper.find('input').prop('onChange');
// test onChangeHandler
})

Although React doesn't allow objects as children, my code clearly says otherwise

So I came across this bizarre behavior — when I try to setState using objects, it only fails in SOME cases.
For example this works,
getUserClaimAmount().then(
userClaimAmount => {
this.setState({ userClaimAmount: userClaimAmount.toString()})
}
);
But the following does not. It will throw an error saying that React children are not allowed to be objects.
getUserClaimAmount().then(
userClaimAmount => {
this.setState({ userClaimAmount: userClaimAmount})
}
);
However, the following works for some reason. "bettingPoolTotal" is the same type as "userClaimAmount" above.
getBettingPoolTotal().then(
bettingPoolTotal => {
this.setState({ total: bettingPoolTotal });
}
);
Below is a screenshot of what the state looks like. It's clear that there's obviously React children that are indeed objects.
Example of React state
You've mixed up the children property and the state of the component.
children property is populated by React when you create the component. Considering the following JSX:
<Parent>
<Child1 />
text here
<Child2 />
</Parent>
The children property of Parent will be an array with three entries: the element created from Child1, string "text here" and the element created from Child2. If you try to pass the plain JavaScript object into the final markup (not into the React component, since it can use it as it want without error, but into native HTML tag), it won't work:
<div>
{{key: value}}
</div>
That's exactly the error you're getting. It seems that there is some element which has this.state.userClaimAmount passed as children entity, and so errors when it gets a plain object instead of string or JSX.
As for the state - it's entirely possible to have plain objects inside it; however, this should be used very cautiously. Consider the following case inside some component which has an obj field in its state:
const obj = this.state.obj;
obj.key = 'value';
this.setState({obj: obj});
The last setState here surprisingly become a no-op, since the state is already changed by this moment - through the reference copied to obj, - and then React, knowing that this is a no-op, doesn't trigger rendering, leaving component the same as before.

React.js input cursor reset with async onChange handler

If I have the following react component:
class Cmpt extends Component {
setValue( e ) {
this.setState({ value : e.target.value });
}
render() {
return <input value={this.state.val} onChange={this.setValue.bind(this)}/>
}
}
Now this works as expected, editing the text doesn't reset the cursor to the end of the input. If I modify it such that the setState happens in async, the cursor reset occurs:
class Cmpt extends Component {
setValue( e ) {
setTimeout( () =>
this.setState({ value : e.target.value }) );
}
render() {
return <input value={this.state.val} onChange={this.setValue.bind(this)}/>
}
}
Obviously the fix is to not call setState synchronously - but I don't understand why the async version doesn't work properly. In my head, the chain of events is as follows:
User adds a character to an input field, changing it from ACD to ABCD
The value of the input DOM node is changed to ABCD to reflect this
Some time passes
setState is called - changing the state of the react component from ACD to ABCD
a render call is triggered - the React diffing algorithm compares the DOM nodes value (ABCD) to the value of this.state.value (ABCD). It sees that both are the same and therefore doesn't trigger a redraw and the cursor is preserved
Clearly my mental model is wrong because a redraw is being triggered - causing the cursor reset.
Please can anyone explain why?
Thanks!
A state changes will always trigger a new render call. After that React itself decides on what to re-render. But it will always get triggered by changing the state. Even if you do
this.setState({})
it will call the render method.
Sorry guys, found a duplicate question that answers my question:
In ReactJS, why does `setState` behave differently when called synchronously?
I can't figure out how to mark my own question as a duplicate unfortunately :(
My mental model of the order of events is wrong. Apparently react triggers a synchronous re-render at the end of every event handler, so render is getting called after the DOM changes, but before the react state has changed - causing a redraw and a cursor reset
Thanks all
T

How to handle focus using declarative/functional style libraries like Redux and ReactJS?

In looking around to see what ways other developers are handling input focus when working with Redux I've come across some general guidance for ReactJS components such as this. My concern however is that the focus() function is imperative and I could see strange behaviours possible where multiple components are fighting over focus. Is there a redux way of dealing with focus? Is anybody dealing with pragmatically setting focus using redux and react and if so what techniques do you use?
Related:
How to set focus on an element in Elm?
Automatically focus input element after creation in purescript-halogen
https://github.com/cyclejs/cycle-core/issues/153
My approach is using ref callback, which is kind of an onRenderComplete of an element. In that callback I can focus (conditionally, if needed) and gain a reference for future focusing.
If the input is rendered conditionally after an action runs, that ref callback should fire a focus, because the ref doesn't exist yet immediately after calling the action, but only after render is done. Dealing with componentDidUpdate for things like focus just seems like a mess.
// Composer.jsx -- contains an input that will need to be focused somewhere else
class Composer extends Component {
render() {
return <input type="text" ref="input" />
}
// exposed as a public method
focus() {
this.refs.input.focus()
}
}
// App.jsx
#connect(
state => ({ isComposing: state.isComposing }),
...
)
class App extends Component {
render() {
const { isComposing } = this.props // or props, doesn't matter
return (
<div>
<button onClick={::this._onCompose}>Compose</button>
{isComposing ? <Composer ref={c => {
this._composer = c
this._composer && this._composer.focus() // issue initial focus
}} /> : null}
</div>
)
}
_onCompose() {
this.props.startComposing() // fire an action that changes state.isComposing
// the first time the action dispatches, this._composer is still null, so the ref takes care of the focus. After the render, the ref remains so it can be accessed:
this._composer && this._composer.focus() // focus if ref already exists
}
}
Why not autoFocus or isFocued prop?
As HTMLInputElement has value as a prop, but focus() as a method -- and not isFocused prop -- I would keep using methods to handle that. isFocused can get a value but if the user blurs from the input, what happens to that value? It'll be out of sync. Also, as mentioned in the comments, autoFocus can conflict with multiple components
So how to decide between props and methods?
For most cases props will be the answer. Methods can be used only in a 'fire and forget' things, such as scrollToBottom in a chat when a new message comes in, scrollIntoView and such. These are one time behaviors that the store doesn't care about and the user can change with an interaction, so a boolean prop won't fit. For all other things, I'd go with props.
Here's a jsbin:
http://jsbin.com/waholo/edit?html,js,output

Resources