ReactNative TextInput value doesn't update on IOS - ios

Expo SDK v27.0.0 based on React-Native 0.55
I have a value I want numeric values only so I filter the input using onChangeText. On Android you see the non-numeric value but it is then erased but on IOS all characters are displayed until another digit is pressed. The code is as follows:-
state = {
appCode: ""
};
render() {
console.log(this.state.appCode);
return (
<View style={styles.container}>
<TextInput
label={"App Code"}
placeholder={"App Code"}
keyboardType="phone-pad"
maxLength={6}
onChangeText={text => {
this.setState({ appCode: text.replace(/\D/g, "") });
}}
value={this.state.appCode}
/>
</View>
);
}
I can see on the console that the appCode value is being updated correctly but the value on the IOS screen doesn't follow the variable.
If I enter
1*#+
that is displayed on the IOS screen (the console shows this.state.appCode is 1 for each keypress) but then pressing, for example, 7 and the value displayed on the screen is updated to
17
All the documentation shows this is the correct way to filter the input, and on Android it works but not on IOS

This is a known bug - only fix for now seems to revert to pre-Jan 2018 React Native:
https://github.com/facebook/react-native/issues/18874
(answering as SO won't let me just comment)

A work around can be - before setting the state on onChangeText, first check that the changed text does not contain any non-numeric value and if does then simply do not update the state. In this way, it should work on both iOS and android and also you will not see the non numeric entries in TextInput.

Related

Why does react-native-maps MapView crash when using custom map markers, Expo, and Apple Maps after rendering four map markers?

I'm working on an Expo React Native project that is using react-native-maps to render a MapView component with a list of Markers. The Markers are custom components that render an Image. When testing using Android and Google Maps everything works perfectly. When testing using an iOS emulator the markers appear but the map runs slowly. When testing using a physical iPhone 7 (and others) the app crashes with no error message. The app always loads correctly until rendering the map, which appears for a second or two before the app crashes. Sometimes the markers will also appear for a split second before the app crashes.
If I set limits on how many items to render the markers will appear as long as the limit is less than five. Similarly I can render each marker if I specify which one to load by id, so I don't think the data is wrong or causing unhandled exceptions. I need all the items in the list to render dynamically, without a limit on how many can be rendered. If I comment out the Image component and the default red pin markers appear on the map without any problems. It seems like the issue has to do with how the markers' Images are rendered dynamically on the Apple map.
I've tried importing the image source, preloading it, requiring it, and using {{uri:url}} format for the Image source parameter. Everything results in the app crashing without an error message. Am I missing something? Is there some way for me to get any kind of error message that can help debug this? Is there a workaround if this is a known issue?
MapView:
<MapView
style={styles.map}
ref={(map) => { currentMap = map; }}
region={region}
onRegionChangeComplete={onRegionChange}
rotateEnabled={false}
loadingEnabled
>
{
eventList.map((marker, index) => {
const { location } = marker.location.geometry;
if (
location.lat <= (region.latitude + region.latitudeDelta / 2)
&& location.lat >= (region.latitude - region.latitudeDelta / 2)
&& location.lng <= (region.longitude + region.longitudeDelta / 2)
&& location.lng >= (region.longitude - region.longitudeDelta / 2)
) {
return (
<MapMarker
key={index}
mapMarker={marker}
handlePress={() => moveMapToCoordinate(marker.location)}
onSelectEvent={onSelectEvent}
/>
);
}
return null;
})
}
</MapView>
MapMarker:
<Marker
coordinate={latLng}
title={title}
onPress={() => handlePress()}
>
<CustomMapMarker
eventType={eventType}
isSanctioned={isSanctioned}
startDate={startAt}
/>
<Callout
style={styles.customCallout}
onPress={() => onSelectEvent(_id)}
>
<ViewEventScreenDetailsHeader
fullEvent={mapMarker}
containerStyle={styles.calloutDetails} />
</Callout>
</Marker>
CustomMapMarker:
const img = require('../../assets/icons/SpikeScreen/map-marker-pickup.png');
return (
<View style={[styles.pickupMarkerContainer, markerContainerStyle]}>
<Image
style={[styles.pickupMarker, markerStyle]}
source={img}
/>
<Text style={styles.dayMonthMarkerText}>{formattedStartDate}</Text>
</View>
)
Got em! Make sure your marker icon image isn't too large, Apple maps renders them differently than google maps.

Mobile Safari (10.3.1) DateTime-Local "Enter a Valid Value" Error

I'm getting an error on the newer versions of iOS' Mobile Safari. This error didn't happen prior to version 10.3 of iOS. Can someone point me in the right direction on this?
Here's the raw HTML and attached are the inspected view and the view of the mobile device (iPhone 7).
Simple solution!
IOS requires a value to be set on an input field with a type of "datetime-local".
Example: <input type="datetime-local" value="2000-07-01T12:00" />
That's it :)
I personally find it nice to set the default value to the users current local time. This has to be formatted in ISOTime without seconds, so the code for this might be something like:
// get the iso time string formatted for usage in an input['type="datetime-local"']
var tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0,-1);
var localISOTimeWithoutSeconds = localISOTime.slice(0,16);
// select the "datetime-local" input to set the default value on
var dtlInput = document.querySelector('input[type="datetime-local"]');
// set it and forget it ;)
dtlInput.value = localISOTime.slice(0,16);
This message is triggered by client-side validation. If you are not using client-side validation then I found you can fix this issue by turning it off using the novalidate attribute as follows:
<form action="/myaction" method="POST" novalidate>
For the whom still struggle with IOS datetime-local same as me.
This caused by IOS set datetime-local value in invalid format when value attribute/property not set or set with invalid one.
<input type='datetime-local' onChange='alert(event.target.value)'>
This will alert with 'yyyy-MM-ddThh:mm:ss' format such as 2018-12-25T12:00:00 which has unallowed string of seconds.
To bypass it, simply set the value with valid form.
const onChange = (event) => {
event.target.value = event.target.value.substr(0, 16);
}
That`s it.
Seems it's a bug in Safari, but you can skip it by using javascript to submit the form.
$("#new_apply_form").submit();
It is a bug in Safari. You should use JS to submit the form instead.
JS: form.submit();
format('Y-m-d\TH:i') worked for me.
This is fixed in iOS 15.4. On iOS 15.3.1, as soon as the user taps on the input, it automatically gets populated with the current date/time and the value will be something like this:
2022-03-18T13:30:45.123
So, unless the user changes the date and/or time to something else, submitting the form will cause a client-side validation error.
But on iOS 15.4, as soon as the user taps on the input, it still automatically gets populated with the current date/time, but now the value will be something like this:
2022-03-18T13:30

React Native ListView slow to load

I have a simple ListView which seems to take a long time to load its items. I've tested in Release mode, and its taking about 3 seconds to load the whole page. If I set initialListSize={1} I can see it building the list item by item.
The ListView JSX code:
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
renderSeparator={(sectionID, rowID) => <View key={`${sectionID}-${rowID}`} style={styles.separator} />}
style={styles.listView}
initialListSize={1}
/>
And the renderRow method:
renderRow(row) {
// console.log ('rendering scale row: ' + row.numerator + ':' + row.denominator );
return (
<TouchableHighlight onPress={() => this.pressRow(row)} underlayColor="grey">
<View style={styles.rowContainer}>
<Text style={styles.rowText}>{row.hasOwnProperty('label') ? row.label : row.numerator + ':' + row.denominator}</Text>
<Text style={styles.rowText}>{row.hasOwnProperty('label') ? row.numerator + ':' + row.denominator : ''}</Text>
</View>
</TouchableHighlight>
);
}
I'm not doing anything very complicated in renderRow... and the data had already been fetched in a previous view and passed in via props.
Is there something I don't know about that could be affecting load/render time?
This is iOS only and on an iPhone 4S. Only 12-21 rows in my tests.
I should also add that the core of the app is using the JUCE C++ framework and running some audio processing in one of the views. I have an audio processing thread that is only run when that view is visible - all other views (including the one with this ListView) will stop the audio processing thread. I've tried also stopping the audio thread in a similar manner but it did not make any difference.
Not sure if this is going to help you, but I can think of three things you can try...
cmd+T will toggle "slow animations" on/off. This is a long shot
If you are writing to the console.log, comment out those lines. They can surprisingly bog things down considerably sometimes.
Turn off dev mode. You shouldn't need to, but worth a shot
Better start using FlatList or SectionList since ListView is now Deprecated.
https://facebook.github.io/react-native/docs/listview.html
There are few Caveats like you must use a FLUX/ REDUX or RELAY. Please Checkout the details here (Features/Caveats)
https://facebook.github.io/react-native/blog/2017/03/13/better-list-views.html

How can I create a discrete slider in react-native (iOS)?

I have a slider component in my react-native app that is nicely restricted to discrete values from 1-5 using the minimum/maximumValue properties and some rounding in onValueChange:
<View>
<Text style={styles.text} >
{this.state.value}
</Text>
<SliderIOS
ref='slider'
style={styles.slider}
value={1}
minimumValue={1}
maximumValue={5}
onValueChange={(value) => {
this.setState({
value: Math.round(value)
});
}}
/>
</View>
What I would like is for the slider handle button to snap to the discrete positions 1,2,3,4,5 rather than move continuously along the slider. Hints appreciated!
For future reference: SliderIOS has been deprecated in the meantime and one should use the Slider component. <Slider> comes with a step attribute:
Step value of the slider. The value should be between 0 and (maximumValue - minimumValue). Default value is 0.
So the solution to the specific question would be to simply set
<Slider
step={1}
...
/>
you're almost there but in the docs of SliderIOS it says:
value number
Initial value of the slider. The value should be between minimumValue
and maximumValue, which default to 0 and 1 respectively. Default value
is 0.
This is not a controlled component, e.g. if you don't update the
value, the component won't be reset to its inital value.
Therefore, it's not possible to control the behavior of the native slider in the way you want it to work.
Solution using another component
However, there is a JavaScript version of the <Slider> component which let's you do it. Since it uses some of the props SliderIOS has, you can easily replace it. Just load the npm module, require it and change <SliderIOS> to <Slider>. Now you need to make some adjustments to your current code:
The property value of the component should reference a variable which can be changed by your code. You can use getInitialState to have a default value.
Add your callback function used for onValueChange to onSlidingComplete. Otherwise you could still have values in between when you release the slider.
I used sliderValue instead of value to make it clearer.
`
getInitialState() {
return {
sliderValue: 1
}
},
onSliderValueChange(value) {
this.setState({
sliderValue: Math.round(value)
});
},
render() {
return(
<View>
<Text style={styles.text} >
{this.state.sliderValue}
</Text>
<Slider
ref='slider'
style={styles.slider}
value={this.state.sliderValue}
minimumValue={1}
maximumValue={5}
onValueChange={this.onSliderValueChange}
onSlidingComplete={this.onSliderValueChange}
/>
</View>
)
}
`
I didn't check my last version the code but it should work like that.

iOS TextInput displayAsPassword doesn't displayAsPassword

I'm building an app to launch across Android, iOS, and desktop simultaneously. The app includes a login that is attached to a vBulletin system and I've run into a significant issue (that the client is adamant must be fixed). On iOS, if you are typing in a TextInput that has its displayAsPassword set to true, it will show plain text while typing. Once you click out of the TextInput, it displays properly.
Here is the code I am using within Flex
<s:TextInput id="inputField" width="100%" styleName="loginFields" text="Password" focusAlpha="0" focusEnabled="false" autoCorrect="false" />
I then attach focus events to the input field that run these functions.
private var defaultText:String = 'Password';
private var passwordDisplay:Boolean = true;
private function focusIn (e:FocusEvent = null):void {
if (this.inputField.text == this.defaultText){
this.inputField.text = '';
}
if (this.passwordDisplay){
this.inputField.displayAsPassword = true;
}
}
private function focusOut (e:FocusEvent = null):void {
if (this.inputField.text == ''){
this.inputField.text = this.defaultText;
if (this.passwordDisplay){
this.inputField.displayAsPassword = false;
}
}
}
There's a lot more code in the file, but this is the only relevant. Basically, on focus in, it checks if the text == the default text. If it does, it empties the field. It then sets displayAsPassword to true. On focus out, it checks if the field is empty. If it is, it resets the field to default and displayAsPassword to false. I know the default text is built in, but I needed more functionality than it offered.
Now, this issue (password displaying as plaintext while focus is on field) is present in iOS only and it doesn't occur in the emulator. It works perfectly and as expected on Android and desktop. I've tried recreating the functionality manually (possible but not ideal because caretIndex is not a TextInput property), I've tried hiding the TextInput and overlaying a field of '•' that match the length of the input (not possible because TextInput is StageText). I'm not sure what else I can try here. Any ideas?
Thanks in advance for any help here.
Specs:
Built and compiled using FlashBuilder 4.6 (but I also have 4.5.1 available to me)
Using Air 3.1
Compiled on OS X Lion
Tested on both 1st and 3rd gen iPads
Using Flex SDK 4.6.0

Resources