React native useRef is giving me undefined - react-native

I am trying to use useRef hook this way.
I want to get the value of textInput with it
let tagsRef = useRef('');
<TextInput
ref={tagsRef}
style={styles.textInputStyle}
placeholder={'Add tags to subscribe to them'}
placeholderTextColor={'lightgray'}
/>
</View>
I am using react-native version:0.63.3
When I
console.log(----tag', tagsRef.current.focus());
It gives me undefined
Any suggestions please?

Check this its Officially RN Documentation
https://reactjs.org/docs/hooks-reference.html#useref
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

It is depending on where do you use the ref. The ref is only filled after the first rendering. You can use it for instance in an event handler on the element.
Another point: You might see that the focus method does not return anything. So when you see an undefined in your console, it is properly related to that problem.
But without further context of your code and your problem, it is hard to give a more detailed answer.

Related

KeyboardAwareScrollView props innerRef scrollToEnd not working

I'm trying to call scrollToEnd() in a screen that uses https://github.com/APSL/react-native-keyboard-aware-scroll-view and I get the error:
"cannot read property 'props' of undefined".
My code looks like this:
let scroll
(at the beginning of the file)
Then, inside the return:
<KeyboardAwareScrollView innerRef={ref => { scroll = ref }}>
my scrollable code
</KeyboardAwareScrollView>
And then there is a button which has
onPress={() => scroll.props.scrolltoEnd()}
Clicking the button gives the error above, which makes me think I'm not using innerRef correctly? Do I need to use useRef instead at some point? Any help is appreciated, thanks!
on KeyboardAwareScrollView use React.useRef
const scrollRef = React.useRef(null);
<KeyboardAwareScrollView
ref={scrollRef}
....
And on your input, listen onContentSizeChange
<Input
onContentSizeChange={(e) => {
if (scrollRef && scrollRef.current) {
scrollRef.current?.scrollToEnd();
}
}}
/>
You must use innerRef instead of ref and it will work.
<KeyboardAwareScrollView
innerRef={(ref) => {
scrollRef.current = ref;
}}

React-native Formik setFieldValue

Here is a simplified version of my code.
Notice the setFieldValue_ and this.setFieldValue_ = setFieldValue;
This code works fine, I'm able to get the output when submit button is clicked.
I'm actually wondering if this is the right way to do it? If not, can you point me to the right direction? Also what is this method called? (assigning class variable to some function and use it within another function)
class MyComponent extends React.Component {
setFieldValue_;
someFunction() {
this.setFieldValue_("name", value);
}
render() {
return (
<Formik
initialValues={{
something: ""
}}
onSubmit={values => console.log(values)}
>
{({
setFieldValue,
}) => {
this.setFieldValue_ = setFieldValue;
<ThirdPartyCustomComponent onChange={this.someFunction} />
}}
</Formik>
}
}
I would personally have the onChange simply call formik set field value there and then rather than using different functions. Strictly speaking you don't want to set the value like that because every re-render is setting the value again.
I would also recommend looking at custom formik inputs using the useField hook - https://jaredpalmer.com/formik/docs/api/useField. This will allow you to write a small wrapper around your third party component and formik. Noticing you have used a class based component you may want to do some short reading into react hooks before throwing yourself into using useField.
Docs example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className='error'>{meta.error}</div>
) : null}
</>
);
};

Given " Invalid left-hand side in assignment expression" in React Native when using onChangeText

My code for a component in React-Native is as follows (please note I am using native-base in case the structure looks weird):
class CardIntInput extends React.Component {
render() {
return (
<Card>
<Form>
<Item regular>
<Label>
{this.props.info}:
</Label>
<Input
keyboardType = 'number-pad'
onChangeText = {(text) => {this.props.data} = text}
/>
</Item>
</Form>
</Card>
)
}
}
Every time I run the code it returns the aforementioned error, which I have isolated to the onChangeText line.
I am pretty new to JS and React Native and I've tried all that I can think of, so I turning to the advice of others.
I hope I'm just forgetting something simple but any advice would be much appreciated
The following will not issue an error:
onChangeText = {(text) => { this.props.data.text = text }}
It is an anti-pattern, however, to assign props data...
See:
Can I update a component's props in React.js?
https://github.com/uberVU/react-guide/issues/7
When using typescript if you have named a component file .ts instead of .tsx this will raise a typescript error. Rename your file to .tsx

How can I check value of the refreshControl prop in jest for my react-native app?

I am trying to do a jest unit test which simply confirms that the refreshControl prop is active in my ScrollView.
I currently have a component that essentially renders the following
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}
/>
}
{children}
/>
It works fine in practice, and in my test, I am checking to confirm that the prop is as expected.
import { RefreshControl } from 'react-native'
const layout = shallow(<Element />)
const refreshControl = <RefreshControl
onRefresh={jest.fn()}
refreshing={false}
/>
expect(layout.prop('refreshControl')).toEqual(refreshControl)
I then get this error when running my test:
Expected value to equal:
<RefreshControlMock onRefresh={[Function mockConstructor]} refreshing={false} />
Received:
<RefreshControlMock onRefresh={[Function mockConstructor]} refreshing={false} />
I assume it's because the instance of the onRefresh function differs, but not sure how to control that. Does anyone perhaps know?
Please find below steps to call Pull to Refresh to cover coverage.
First, get access to the scrollView component through testID
Fetch refresh control present in the scroll view component
call onRefresh from test case
I have added the below Example for reference.
test('refresh control', async () => {
const props = createTestProps();
const component = render(
<Provider store={store}>
<ScrollViewComponent {...props} />
</Provider>
);
const scrollView = component.getByTestId('refreshControl');
expect(scrollView).toBeDefined();
const { refreshControl } = scrollView.props;
await act(async () => {
refreshControl.props.onRefresh();
});
});
Just realised that I need to check that the function is the same one I pass into the component, in the test, so basically, the function in the component that matches.
this._onRefresh
MW UMESH's answer helped me quite a lot (and I therefore upvoted it) but since you get the refreshControl prop synchronously, you can remove the async test here.
So replace
await act(async () => {
refreshControl.props.onRefresh();
});
With
refreshControl.props.onRefresh();
And the reload mechanism will trigger.

React Native - Difference between onChange vs onChangeText of TextInput

I'm not sure when to use onChange vs onChangeText in a TextInput component. I know onChangeText accepts the changed text as an arg in the callback, but is that why you would use onChangeText, since you can then update state within the callback?
UPDATE 26.08.2019
Since the initial version of the answer, TextInput's API has changed, and answer below is no longer valid. I haven't worked with react-native for more than 2 years now, so I can't really tell which version had these changes.
Regarding the answer, onChangeText is still a simple prop, that gives whatever is the value of the input field on every change.
onChange on the other hand, passes an event with { nativeEvent: { eventCount, target, text} } (as mentioned in the comment to this answer). Now, I cannot tell with confidence, why do you need eventCount and target. I can only state, that eventCount is increased every time you interact with TextInput component (character added, removed, all deleted, value pasted) and target is a unique integer for that TextInput field. And text is the same value as in onChangeText
So basically, I would suggest to use onChangeText, as a more straight forward prop.
If you want to accomplish functionality like in the old answer(below), you can create custom component, that wraps TextInput and receives custom properties and passes them to the onChange prop later. Example below:
const MyTextInput = ({ value, name, type, onChange }) => {
return (
<TextInput
value={value}
onChangeText={text => onChange({ name, type, text })}
/>
);
};
And then use it whenever you need to use TextInput
handleChange(event) {
const {name, type, text} = event;
let processedData = text;
if(type==='text') {
processedData = value.toUpperCase();
} else if (type==='number') {
processedData = value * 2;
}
this.setState({[name]: processedData})
}
<MyTextInput name="username" type="text" value={this.state.username} onChange={this.handleChange}}>
<MyTextInput name="password" type="number" value={this.state.password} onChange={this.handleChange}}>
OLD ANSWER
onChangeText is basically a simplified version of onChange, so you can easily use it, without the hassle of going through event.target.value to get changed value.
So, when should you use onChange and when onChangeText?
If you have simple form with few textinputs, or simple logic, you can go straight away and use onChangeText
<TextInput value={this.state.name} onChangeText={(text) => this.setState({name: text})}>
If you have more complicated forms and/or you have more logic in handling data (like handling text differently from number) when user changes input, then you are better of with onChange, because it gives you more flexibility. For example:
handleChange(event) {
const {name, type, value} = event.nativeEvent;
let processedData = value;
if(type==='text') {
processedData = value.toUpperCase();
} else if (type==='number') {
processedData = value * 2;
}
this.setState({[name]: processedData})
}
<TextInput name="username" type="text" value={this.state.username} onChange={this.handleChange}}>
<TextInput name="password" type="number" value={this.state.password} onChange={this.handleChange}}>
Use it like this:
<Input label='First Name' onChange={this.onChange} value={this.state.first}/>
onChange = (event) => {
const {eventCount, target, text} = event.nativeEvent;
this.setState({first:text});
};
The target attribute seems useless. It doesn't look like you can attach data attributes to react-native elements and retrieve them from the target element like you can in react because the app is not a browser.
With react, we're told it's better practice to not attach inline functions to the onChange event for performance reasons. We're supposed to use custom props or data-* attributes on the HTML element and retrieve the information from e.target inside the onChange handler.
But with react-native it seems this format of passing data is actually acceptable:
<Input
label='First Name'
onChangeText={text=>this.onChange('first',text,'anotherValueIWantToPass')}
value={this.state.first}/>
onChangeText gives you just the string as the argument for the callback.
onChange gives you the synthetic event as the argument.