React Native components seem to be sharing a state - react-native

I'm having an issue with React-native where I have a component TouchTimer which uses an AnimatedTimer component. This timer is supposed to start and stop when it is tapped, which it does, however all of the TouchTimer components I add to a page will start and stop whenever any of them are tapped, rather than only affecting the tapped component.
Below is a snippet of my component:
TouchTimer.tsx
export class TouchTimer extends React.Component<TouchTimerProps> {
state: {
...
paused: boolean,
}
constructor(props) {
super(props);
...
this.state = {
...
paused: true,
}
}
startStop() {
this.setState({paused: !this.state.paused});
}
render() {
const { time } = this.props;
return (
<TouchableHighlight onPress={() => this.startStop()}>
<View>
<AnimatedTimer
...
time={time}
pause={this.state.paused}
/>
<View style={styles.timeContainer}>
<Text style={styles.time}>{this.state.remaining}</Text>
</View>
</View>
</TouchableHighlight>
)
}
}
And here is a snippet of the screen containing these components:
Details.tsx
import { TouchTimer } from '../components/TouchTimer';
...
export class RecipeDetailsScreen extends React.Component<NavigationInjectedProps> {
...
{this.state.steps.map(step => (
<List.Item
key={step.id}
title={"Step " + step.index}
style={styles.step}
description={step.short_desc}
right={() => (step.time > 0 &&
<TouchTimer
time={step.time * 60000}
/>
)}
/>
)
}
I have tried wrapping the TouchTimer components in a View and changing the paused boolean to a prop, to no avail.
I have also tested to see if this issue appears when the components are not siblings, and when they are not produced as the result of a callback, and the issue still persists in both these cases.
If anybody has any advice or answers on how to make these timers independent I would very much appreciate it!

Curiously that component seems to be implemented with a global pauseFlag that applies to all component instances. See https://github.com/dalisalvador/react-native-animated-timer/blob/master/src/Components/AnimatedTimer.js#L34
So I don't think you're doing anything wrong here, this is a limitation of the library code that is coupling all instances of your timer to the same pauseFlag value.

Related

accessing props using ref call back

I am scratching my head hard; trying to figure out what's wrong in below snippet.
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
class MyButton extends React.Component {
setNativeProps = (nativeProps) => {
alert(JSON.stringify(this._root.props)) //able to get this.v here
}
render() {
return (
<View ref={cc => {this._root = cc; this.v = 100 }} me="tom">
<Text ref={component => { this._root1 = component;}} style={{margin:55}} onPress={()=>this.setNativeProps({text:'fgfg'})}>{this.props.label} </Text>
</View>
)
}
}
export default class App extends React.Component {
render() {
return (
<TouchableOpacity >
<MyButton label="Press me!" />
</TouchableOpacity>
)
}
}
basically trying to get the props from <View> element i.e. this._root.props using ref callback
though this._root1.props works perfectly fine all the times.
could someone help me figure out what's the problem with it?
EDIT:
I am even able to see this._root but even not this._root.props.me.
Could you try not to do
alert(JSON.stringify(this._root.props))
instead just do
alert(this._root.props)
i.e. remove JSON.stringify
The reason it's not working is because View is having a child element with-in itself while with using Text it's not having any child in it.

react native, undefined is not an object (evaluating 'this.state.thing = input')

I am trying to set the onSubmitEditing function of a TextInput object to a custom function, here is my code:
export default class Component4 extends Component {
constructor(props) {
super(props);
this.state = {thing: 'asdf'};
this.func = this.func.bind(this);
}
func(input){
this.setState({thing: input.target.value});
// I will eventually do more complicated stuff
}
render(){
return (
<View style={{padding: 30}}>
<TextInput placeholder="default" onSubmitEditing={this.func}/>
<Text>{this.state.thing}</Text>
</View>
);
}
}
I would like the content of the TextInput to be passed to the function 'func' so that it can change the state of 'thing'. I realize I can just use an arrow function to achieve this and skip having function entirely however like it says in the comment in func I intend to add more complex behaviour there. Thanks for the help
When you declare your function that way it doesn't have access to this because of scoping. Use es6 fat function to give it access to this. Also use setState function instead of direct assignment
func = (input) => {
this.setState({thing: input});
//more complicated stuff here
};
Also change TextInput to use onChange instead of onSubmitEdit
You have to bind your methods to the constructor. You also need a method/function to handle text changes as well as one to handle collecting a result. edit: I guess now that I understand what you're going for, the button was unnecessary.
export default class Component4 extends Component {
constructor(props) {
super(props);
this.state = {myInput: '', myResult: ''};
this.onChange = this.onChange.bind(this);
this.onPress = this.onPress.bind(this);
}
onChange(input){
this.setState({myInput: input.target.value});
// I will eventually do more complicated stuff
}
onPress() {
this.setState({myInput: '', myResult: this.state.myInput});
}
render(){
return (
<View style={{padding: 30}}>
<TextInput name='myInput' placeholder="default" onChange={this.onChange}/>
<Text>{this.state.myResult}</Text>
<Button onPress={this.onPress} title='Click me!' />
</View>
);
}
}

react-native scrollView - scrollToEnd - on Android

I'm trying to call a function that will fire upon onFoucs on TextInput that will scroll the scrollView all the way down (using scrollToEnd())
so this is my class component
class MyCMP extends Component {
constructor(props) {
super(props);
this.onInputFocus = this.onInputFocus.bind(this);
}
onInputFocus() {
setTimeout(() => {
this.refs.scroll.scrollToEnd();
console.log('done scrolling');
}, 1);
}
render() {
return (
<View>
<ScrollView ref="scroll">
{ /* items */ }
</ScrollView>
<TextInput onFocus={this.onInputFocus} />
</View>
);
}
}
export default MyCMP;
the component above works and it does scroll but it takes a lot of time ... I'm using setTimeout because without it its just going down the screen without calculating the keybaord's height so it not scrolling down enough, even when I keep typing (and triggering that focus on the input) it still doesn't scroll all the way down.
I'm dealing with it some good hours now, I did set the windowSoftInputMode to adjustResize and I did went through some modules like react-native-keyboard-aware-scroll-view or react-native-auto-scroll but none of them really does the work as I need it.
any direction how to make it done the right way would be really appreciated. thanks!
Rather than using a setTimeout you use Keyboard API of react-native. You add an event listener for keyboard show and then scroll the view to end. You might need to create some logic on which input is focused if you have more than one input in your component but if you only have one you can just do it like the example below.
Another good thing to do is changing your refs to functional ones since string refs are considered as legacy and will be removed in future releases of react. More info here.
class MyCMP extends Component {
constructor(props) {
super(props);
this.scroll = null;
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
}
_keyboardDidShow() {
this.scroll.scrollToEnd();
}
render() {
return (
<View>
<ScrollView ref={(scroll) => {this.scroll = scroll;}}>
{ /* items */ }
</ScrollView>
<TextInput />
</View>
);
}
}
export default MyCMP;
If you have a large dataset React Native docs is telling you to go with FlatList.
To get it to scroll to bottom this is what worked for me
<FlatList
ref={ref => (this.scrollView = ref)}
onContentSizeChange={() => {
this.scrollView.scrollToEnd({ animated: true, index: -1 }, 200);
}}
/>

React Native: Updating state in onLayout gives "Warning: setState(...): Cannot update during an existing state transition"

I have a component in React Native which updates it's state once it knows what size it is.
Example:
class MyComponent extends Component {
...
render() {
...
return (
<View onLayout={this.onLayout.bind(this)}>
<Image source={this.state.imageSource} />
</View>
);
}
onLayout(event) {
...
this.setState({
imageSource: newImageSource
});
}
...
}
This gives the following error:
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
I guess the onLayout function is called while still rendering (which can be good, the sooner the update, the better). What is the correct way to solve this problem?
Thanks in advance!
We got around this by using the measure function, you will have to wait until the scene is fully complete before measuring to prevent incorrect values (i.e. in componentDidMount/componentDidUpdate). Here's an example:
measureComponent = () => {
if (this.refs.exampleRef) {
this.refs.exampleRef.measure(this._logLargestSize);
}
}
_logLargestSize = (ox, oy, width, height, px, py) => {
if (height > this.state.measureState) {
this.setState({measureState:height});
}
}
render() {
return (
<View ref = 'exampleRef' style = {{minHeight: this.props.minFeedbackSize}}/>
);
}
Here is a solution from documentation for such cases
class MyComponent extends Component {
...
render() {
...
return (
<View>
<Image ref="image" source={this.state.imageSource} />
</View>
);
}
componentDidMount() {
//Now you can get your component from this.refs.image
}
...
}
But for my opinion it's better to do such things onload

react-native-router-flux: Navbar buttons need to access scene component methods

I am inserting a rightButton into react-native-router-flux using the renderRightButton api. The problem I ran into is that the rightButton needs to access a method within the component.
class App extends React.Component {
render() {
return(
<Router>
<Scene
key="firstScene"
component={FirstScene}
title="First scene"
rightTitle="Apply"
renderRightButton={this.getRightButton}
/>
</Router>
)
}
getRightButton() {
return(
// someMethodOnFirstSceneComponent lives on the FirstScene component
<View onPress{this.someMethodOnFirstSceneComponent}>
<Text>
Invoke Function from Scene Component
</Text>
</View>
)
}
};
One option would be to put all of the data currently in the state of FirstScene into a redux reducer and then put the someMethodOnFirstSceneComponent on the FirstScene component. However, I am trying to keep state within components and not rely on reducers because from my experience, it keeps things easier to reason about.
I've found a way to do that. Not a very nice solution, but it works. I'm setting onRight method in the component constructor
constructor(props) {
super(props);
this.props.component.onRight = () => {
console.warn("call from my onRight function")}
}
}