and of course sorry if the question is somewhat dumb.
In the app I'm developing a user should be able to swipe on the TextInput. Since TextInput only listens to taps I used this gist: https://gist.github.com/MikeShi42/87b65984f0a31e38d553cc056fcda017
(BTW #Michael Shi thanks a ton)
However, once I changed TextInput to SlideTextInput the Clear button ceased to work.
clearInput() {
this.setState({text: ''});
}
render() {
return (
<Button name='clear' action={this.clearInput} />
<SlideTextInput
style={styles.input}
ref='input'
onChangeText={(text) => this.setState({text: text})}
placeholder={this.state.placeholder}
value={this.state.text}
multiline={true}
returnKeyType='done'
blurOnSubmit={true} />
)
}
I also tried this.refs.input.setNativeProps({text: ''}); instead of just passing a new value prop (that should be — and was — sufficient for normal TextInput), and calling forceUpdate(), but again to no avail. I don't see much changes in SlideTextInput.js compared to the original TextInput component, but I must be missing something that would explain such bad behaviour?
UPD: the answer was pretty simple in the end. Instead of linking the component to its native counterpart (ref={this._setNativeRef}) like original TextInput does, SlideTextInput has it ref'ed to a string (ref="input"). I changed it back and voila.
Looking at the code it seems that the value props is not being sent to the original TextInput. It is extending the TextInput but it is returning another component without sending the value prop.
Try:
this.refs.input.setText('');
The answer was pretty simple in the end. Instead of linking the component to its native counterpart (ref={this._setNativeRef}) like original TextInput does, SlideTextInput has it ref'ed to a string (ref="input") (it's not about props in my code it's about SlideTextInput.js file itself). I changed it back and voila.
Related
I have a state:
const [userName, setUserName] = useState('');
Next, I have the TextInput as:
<TextInput style={styles.value}
keyboardType={'default'}
placeholder={'Your Name'}
value={userName}
editable={true}
onChangeText={(value) => {
setUserName(value)
}} />
The on a button's onPress event I'm calling this function:
function saveButtonPressed() {
alert("The Value of Name is " + userName);
}
The problem is I can see the vvalue getting updated in the text field, but in the alert I still see '' and if I save tthe coe again the second time it shows the updated value.
The reason they don't show the current value has to do with your functions forming closures. Even if the value of userName changes, it is still a const and since the function where this was updated still has scope in the 1st re-render the updated value wont show. Instead when the scope changes outside the closure thats when you get the updated value in the 2nd re-render.
The first link has solutions for this.
The second link has a bit more information about hooks.
These should answer your question in detail. https://stackoverflow.com/a/58877875/9745240 https://stackoverflow.com/a/54069332/9745240
And a read about closures should help you.
The following is a first attempt at learning to simply change the style of an element onPress in react native. Being well versed in web languages I am finding it difficult as it is not as straight forward.
For reasons as yet unknown, the element requires two clicks in order to execute.
export class NavTabItem extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
}
this.NavTabAction = this.NavTabAction.bind(this)
}
NavTabAction = (elem) => {
elem.setState({active: !elem.state.active})
}
render() {
return (
<TouchableOpacity
style={this.state.active ? styles.NavTabItemSelected : styles.NavTabItem}
onPress={()=> {
this.NavTabAction(this)
}}>
<View style={styles.NavTabIcon} />
<Text style={styles.NavTabLabel}>{this.props.children}</Text>
</TouchableOpacity>
);
}
}
Other issues:
I also have not worked out how a means of setting the active state to false for other elements under the parent on click.
Additionally, Is there a simple way to affect the style of child elements like with the web. At the moment I cannot see a means of a parent style affecting a child element through selectors like you can with CSS
eg. a stylesheet that read NavTabItemSelected Text :{ // active style for <Text> }
Instead of calling elem.setState or elem.state, it should be this.setState and elem.state.
NavTabAction = (elem) => {
this.setState(prev => ({...prev, active: !prev.active}))
}
And instead of passing this in the onPress, you should just pass the function's reference.
onPress={this.NavTabAction}>
You should also remove this line because you are using arrow function
// no need to bind when using arrow functions
this.NavTabAction = this.NavTabAction.bind(this)
Additionally, Is there a simple way to affect the style of child elements like with the web
You could check styled-component, but I think that feature don't exists yet for react native. What you should do is pass props down to child components.
Thanks to everyone for their help with this and sorting out some other bits and pieces with the code.
The issue in question however was that the style was changing on the second click. A few hours later and I have a cause and a solution for anyone suffering from this. Should any of the far more experienced people who have answered this question believe this answer is incorrect or they have a better one, please post it but for now here is the only way I have found to fix it.
The cause:
Using setState was correctly re rendering the variables. This could both be seen in the console via console.log() and directly outputted in the render making them visible.
However, no matter what was tried, this did not update the style. Whether it was a style name from the Stylesheet or inline styles, they would update on the second click rather than the first but still to the parameters of the first. So if the first click should make a button turn from red to green, it would not do so even though the new state had rendered. However if a subsequent click should have turned the button back to red then the button would now go green (like it should have for the first click). It would then go red on the third click seemingly always one step behind the status passed to it.
Solution
To fix this, take the style off the the primary element (forgive terminology, someone edit), in my case, the TouchableOpacity element. Add in a child View element and place the styles on that View element instead along with the ternary operator and wallah.
It seems any change to status on the effective master element or container if you prefer, only takes affect after another render, not that contained in setStatus.
Final code:
export class NavTabItem extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
}
}
NavTabAction = () => {
this.setState({active: !this.state.active})
}
render() {
this.state.active == true ? console.log("selected") : console.log("unselected")
return (
<TouchableOpacity onPress={this.NavTabAction}>
// added View containing style and ternary operator
<View style={this.state.active == true ? styles.NavTabItemSelected : styles.NavTabItem}>
<View style={styles.NavTabIcon} />
<TextCap11 style={styles.NavTabLabel}>{this.props.children}</TextCap11>
</View>
// End added view
</TouchableOpacity>
);
}
}
I know that the practice of react native is to use onChangeText for change value as follow.
<TextInput
onChangeText = {(text)=> this.setState({myText: text})}
value = {this.state.myText}
/>
However, I wonder if i could do something like this. To summarise, I want to loop through all the TextInput and get the ref and value. I just want to do things in javascript way as follow. Is that possible to be done?
<TextInput id="id1" ref={ref => (this.remark = ref)} />
<TextInput id="id2" ref={ref => (this.remark1 = ref)} />
onSubmit = () => {
forEach(TextInput in component) {
console.log(TextInput.id) <- note id is custom property
console.log(TextInput.refName)
console.log(TextInput.value)
}
}
Yes, but I wouldn't recommend this approach. You could simply create an array of refs and loop through it in your onSubmit function.
forEach(TextInput in component) {
This is not possible in any javascript environment (not only because forEach and for..in syntax is different but also you can't expect to be able to loop over component elements by type (?) and get them)
What you want to do is not javascript way but old style browser way:
there are no ids in react-native
technically you could get uncontrolled TextInput's value like you would do in plain react in browser environment (via this.textInputRef._lastNativeText) but it's discouraged in react-native, most likely because because instead of DOM react native has to render to native views which all differ depending on the platform (iOS, Android, Windows, etc) but have to work the same
So it's not only impossible in react native but in plain react as well. It looks to me like you want to create a form, and sooner or later you will find out that you want validation or transformation on your inputs and switch back to controlled components anyway
I am developing a simple react-native app and am encountering an issue on TouchableHighlight:
When pressing the TouchableHighlight, a new screen is displayed (using StackNavigator from react-navigation). After pressing the back-button and returning to the original screen, the TouchableHighlight still has a black background-color - meaning, that it is still highlighted.
My questions are:
Is there a way to manually deactivate the highlighting of a TouchableHighlight-component? That way I could disable the highlighting after onPress has run.
What could be possible reasons to why the TouchableHighlight stays highlighted? I am using it on other parts of my app without navigation, and I could imagine that it has to do with that.
The TouchableHighlight exists within a FlatList. The renderItems-method looks like the following:
let handlePress = () => {
this.props.navigation.navigate('DetailsScreen');
};
return <TouchableHighlight
onPress={handlePress}>
<Text>Some Text</Text>
</TouchableHighlight>;
If you need/want any further information, please let me know. I've tested the code on android, using the Genymotion-emulator with Marshmallow.
Versions are:
node -v: 8.9.4
npm -v: 5.6.0
react-native-cli: 2.0.1
react-native: 0.54.2
react-navigation: 1.5.2
Build environment: Windows 10 64-bit
At this point, I'm quite certain that the error is somewhere in my code, as TouchableHighlight works correctly on other parts of my app, and it propably has to do with the navigation-call, but I was unable to pinpoint, why exactly. I've made sure that there are no exceptions or anything like that in my app, and that the onPress-method therefore finishes successfully.
You can replace Touchable Highlight with Touchable opacity and simply set activeOpactity prop with value 1. It will not highlight the press.
<TouchableOpacity activeOpacity={1}>....</TouchableOpacity>
After using the tip from #Kartiikeya and exchanging TouchableHighlight with TouchableOpacity and back to TouchableHighlight, it now works as expected:
Now, after onPress has been executed, the button (be it a TouchableOpacity or a TouchableHighlight) looses its effect.
I am not sure, why it works now. The obvious reason would be, that a recompilation of the source code fixed errors - but I recompiled it for writing the original question multiple times before, so that that cannot be an option. Other users I would suggest to clear any cache possible, and especially do the following steps:
Close and reopen the android emulator / restart your testing device
Restart the build PC
Recompile all source code
Check in your console for errors and/or exceptions (obviously)
Replace TouchableHighlight with TouchableOpacity, recompile, check if the error still exists - and if not, reexchange TouchableOpacity to TouchableHighlight
You can replace Touchable Highlight with Touchable opacity. It won't highlight the press.
return <TouchableOpacity
onPress={handlePress}>
<Text>Some Text</Text>
</TouchableOpacity >;
For me, i needed to disable the highlight effect after onLongPress has been fired. You can simply change the key of the touchable using a re-render when you want to remove it.
Here's an example:
<TouchableHighLight
onPress={this.pressRow}
style={styles.outerContainer}
onLongPress={() => this.setState({ onLongPressed: true })}
onPressOut={() => this.setState({ onLongPressed: false })}
key={`long-pressed-${this.state.onLongPressed ? 'yes' : 'no'}`}>
<View style={styles.innerContainer}>
{rowText}
{rowIcon}
</View>
</TouchableHighLight>
Following Leonardo Lusoli's answer, there one thing you should also add is
useEffect(() => {
if(isLongPressed){
setIsLongPressed(false)
}
}, [isLongPressed])
This step is necessary because
when the first onLongPress event is fired it will set isLongPressed to true and thus changing the key the component is re-rendered and is identifies as a new component and previour event listners are discareded so the onPressOut will not be fired. So
when isLongPressed is set to true the component re-renders and then immediatietly we set it's value to false and thus we get the expected behaviour. Otherwise we will get the unexpected behaviour followed by one expected behaviour.
I experience a behaviour where TouchableHighlight and TouchableOpacity reacts visually upon render (onPress is not being called).
One thing is that it looks just a little strange, when I enter the page and my button make a small "blink". This is strange but tolerable. The more frustrating part is that if I alter state for the parent component and thus invoke a re-render(), the button will "blink" again, making all buttons blink whenever I alter state.
Pushing the buttons alters page state, and thus pushing a button makes both buttons "blink".
I use react-redux, but this should not affect this behaviour.
The code below is just for illustration.
render()
{
return(
<View>
<ToucableHightlight> //Click here changes state
<Content/>
</ToucableHightlight>
<ToucableHightlight> //Click here changes state
<Content/>
</ToucableHightlight>
<View>
);
}
Add activeOpacity in TouchableOpacity and it will force to not blink.
<TouchableOpacity style={styles.opecity} activeOpacity={1}>
I solved the problem. Earlier during my render function i defined the "Content"-components, resulting in new (but alike) components being defined during each update. Placing the definitions of "Content" outside of the render function fixed it, so that the components no longer flashes when the page is re-rendered.
This explains why my component was rendered as a new component upon each render in the parent component, but it does not explain why a TouchableHighlight blinks during its initial render.
Buttons blinking during initial render is acceptable to me - buttons blinking upon any state-change is not.
So I am sufficiently happy now.
Not sure if it's because I'm running a later version, but I found this blinking behavior happens only on the first click.
My solution was putting the code that triggers rerendering in a setTimeout
<TouchableOpacity
onPress={function() {
setTimeout(function() {
_this.setState({myState: 'someValue'})
});
}}
>