I'm using the TextInput core component of react native for getting user email. The onChangeText stores the text in local state. The validation function is subscribed to the textInput's onBlur prop and validates the email in the state variable.
<TextInput
label="Email"
keyboardType="email-address"
value={email}
onChangeText={(text) => setEmail(text.trim())}
onBlur={() => validateEmail()}
onFocus={() => setErrEmail(undefined)}
/>
The problem is running the validation with autofill. If the user selects the autofill option then there is a slight delay in the text updating in state but the validation runs immediately.
This results in the validation failing. After the slight delay the input field even shows the autofill value, which is worse, because usually that value should have been validated.
How can I resolve the inconsistency between the validation running immediately with empty value but the state getting updated with a delay?
I have tried using setTimeout for the validation but it is resulting in app crashes.
Is there a way to resolve this using the native TextInput or will I have to look at another library to handle form validations with autofill?
Related
I'm attempting to write some tests for my react native app. A component in my app focuses a TextInput after a parent Pressable receives a press. I'm using a ref to identify and focus the TextInput element.
My desired functionality works just fine when running my app through expo, but my test is failing because it seems that the onFocus event is not being called.
Why isn't ref.current.focus() being called when running tests via the #testing-library/react-native library?
Here's my code:
Foo.js
import {Pressable, TextInput} from "react-native";
import {useRef} from "react";
const Foo = (props) => {
const inputRef = useRef()
return (
<Pressable testID={"pressable"} onPress={() => inputRef.current.focus()}>
<TextInput ref={inputRef} testID={"input"} onFocus={props.onFocus} />
</Pressable>
)
}
export default Foo;
Foo.test.js
import { render, screen, fireEvent } from '#testing-library/react-native';
import Foo from "./foo";
test('onFocus is called after press', () => {
const mockFocus = jest.fn();
render(<Foo onFocus={mockFocus} />)
// fireEvent(screen.getByTestId("input"), 'focus');
fireEvent.press(screen.getByTestId("pressable"));
expect(mockFocus).toHaveBeenCalled()
});
This test fails with the following message:
Error: expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
I created an issue on the RNTL GitHub page and received a response from a contributor. Copying and pasting here for easy reference for anyone who might stumble upon this thread.
This is expected as React Test Renderer which we use for rendering
does not support refs. Check #1006 for more details.
Since the behaviour of focus() triggering onFocus event would probably
involve roundtrip to native side (from JS side), then you actually
cannot test such behaviour using RNTL as we do not run native code (see
here for details).
That leaves you with following options:
Call fireEvent.focus to manually trigger onFocus on relevant
TextInput. Recommended in case focus assertion would be a part of a
step in a bigger test.
Use createNodeMock to pass ref creating function to render method, More details here. Recommended in case you
just want to assert pressing button triggers onFocus.
In the android emulator, the following RN code does nothing (no log, no handler called)
<Pressable style={[styles.fullscreenContainer]}
onPressIn={()=> {console.log("onPress");this.props.touchHandler()}}>
However, if I insert a onPress handler, in otherwise exactly the same code, both logs appear and both handlers are called when the component is pressed.
<Pressable style={[styles.fullscreenContainer]}
onPress= {()=> {console.log("onPress"); this.props.touchHandler()}}
onPressIn={()=> {console.log("onPressIn");this.props.touchHandler()}}>
Why does onPressIn not work on its own?
FYI onPressIn works correctly on iOS in the expo client
onPressIn is followed by onPress, but if you haven't given onPress any logic to perform, then nothing will happen. You'll only get an onPressIn response, even though onPress is technically still triggered behind the scenes. (It's just a blank onPress).
I'm using the Contentful CMS JS SDK and the code below does not update my React Hook state. It's more of a promises combined with react hooks combined with React render issue - I believe. I understand that the promise sets the hook value when it is resolved at a later time, but by that time my component already rendered with the initial data (empty string), so if this is the case, how can I make my component re-render when the react hook state is set with the correct value from the promise, so that my Button displays it.
Any help appreciated. Thanks
Problem lies within here.
...
import client from 'contentful'
const [name, setName] = useState('')
client.getEntry(data.fields.store.sys.id)
.then(entry => setName(entry.fields.name))
.catch(console.error)
console.log('STORE URL', name) // name is not set
...
<Button title={name} /> // name still not set
....
I just tried your code and it seems to work for me. :)
https://codesandbox.io/embed/react-hooks-demo-e4wgo
I think you should use useEffect though as described in this article.
i know its possible to store data using local storage for a web app but how would i do this for a react native app. the app I've made allows the user to enter a phone number into the input field and it will call that number. I also have a login page. I would like the last number they entered into the input field still be there when they re open the app. heres the code below
<View>
<Text style={styles.phoneNumberTitle}>PHONE NUMBER</Text></View>
<TextInput style={styles.inputs}
ref={(el)=>{this.recipient=el;}}
onChangeText={(recipient)=>this.setState({recipient})}
value={this.state.recipient}/>
<Button title="CALL"
style={styles.callButtCont}
onPress={this.setTimer.bind(this)} />
UPDATE:
AsyncStorage is Deprecated. Use react-native-community/react-native-async-storage instead.
ORIGINAL ANSWER:
AsyncStorage is a simple, unencrypted, asynchronous, persistent, key-value storage system that is global to the app. It should be used instead of LocalStorage.
import AsyncStorage at the top :-
import { AsyncStorage} from 'react-native'
set like this :-
AsyncStorage.setItem("recipient", this.state.recipient);
and access like this :-
AsyncStorage.getItem("recipient").then((value) => {
alert("Get recipient >> ", value);
}).done();
Reference: https://facebook.github.io/react-native/docs/asyncstorage.html
Using React-Native (0.19) and Redux, I'm able to navigate from scene to scene in React Components like so:
this.props.navigator.push({
title: "Registrations",
component: RegistrationContainer
});
Additionally I'd like to be able push components to the navigator from anywhere in the app (reducers and/or actions).
Example Flow:
User fills out form and presses Submit
We dispatch the form data to an action
The action sets state that it has started to send data across the wire
The action fetches the data
When complete, action dispatches that the submission has ended
Action navigates to the new data recently created
Problems I'm seeing with my approach:
The navigator is in the props, not the state. In the reducer, I do not have access to the props
I need to pass navigator into any action that needs it.
I feel like I'm missing something slightly simple on how to access Navigator from actions without sending in as a parameter.
In my opinion the best way to handle the navigation with Redux is with react-native-router-flux, because it can delegate all the navigation logic to Redux:
You can change the route from the reducer;
You can connect the router to the store and dispatch its own actions that will inform the store about route changes (BEFORE_ROUTE, AFTER_ROUTE, AFTER_POP, BEFORE_POP, AFTER_DISMISS, BEFORE_DISMISS);
An example
Here is an example on how easily you can save the currently focused route in the store and handle it in a component (that will be aware of being focused):
1. Connect a <Route> to Redux
Connecting a <Route> to Redux is easy, instead of:
<Route name="register" component={RegisterScreen} title="Register" />
you might write:
<Route name="register" component={connect(selectFnForRegister)(RegisterScreen)} title="Register" />
You can also simply connect the component itself in its own file like you usually do.
2. Connect a <Router> to Redux
If you need to inform Redux of the navigation status (i.e. when you pop a route) just override the <Router> component included in react-native-router-flux with a connected one:
import ReactNativeRouter, { Actions, Router } from 'react-native-router-flux'
const Router = connect()(ReactNativeRouter.Router)
Now when you use a <Router> it will be connected to the store and will trigger the following actions:
Actions.BEFORE_ROUTE
Actions.AFTER_ROUTE
Actions.AFTER_POP
Actions.BEFORE_POP
Actions.AFTER_DISMISS
Actions.BEFORE_DISMISS
Take a look at this for an example.
3. Catch the interested actions in your reducer
For this example I have a global reducer (where I keep the information needed by all my app) where I set the currentRoute:
case Actions.AFTER_ROUTE:
case Actions.AFTER_POP:
return state.set('currentRoute', action.name)
Now the reducer will catch every route change and update global.currentRoute with the currently focused route.
You also can do many other interesting things from here, like saving an history of the navigation itself in an array!
4. Update your component on focus
I'm doing it on componentDidUpdate of my component of the route named payment.
If global.currentRoute is payment and the previous global.currentRoute was different, than the component has just been focused.
componentDidUpdate(prevProps) {
const prevRoute = prevProps.global.currentRoute
const currentRoute = this.props.global.currentRoute
if (currentRoute === 'payment' && prevRoute !== currentRoute) {
this.props.actions.doSomething()
}
}
P.S.: Remember to check currentRoute === 'payment', otherwise you'll start doSomething() on every route change!
Also, take a look a the README.md for learning other stuff about the integration with Redux.
Hope it helps, long live Redux!
Maybe you could pass the information about title and component in an action and the component with the navigator can then push the right scene with the information of the state.