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

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")}
}
}

Related

React Native components seem to be sharing a state

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.

How change a variable of a parent in react-native

I want to change client = {state:0}
You can access it by using this.client.state
I also have a child which contains a button.
I'm trying to change this 'state' variable when you press the button.
For some reason everything I find everything on the internet not working for me.
I've been stuck at it for 5 hours and I think it's time to ask help myself
import React from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity } from 'react-native';
import Home from './Components/Home';
import Activity from './Components/Activity';
export default class App extends React.Component {
client = {state:0}
render() {
if(this.client.state == 0){
return(
<View style={{flex: 1}}>
<Home />
<Child />
</View>
);
} else {
return(
<View style={{flex: 1}}>
<Activity />
<Child />
</View>
);
}
There are different ways of doing this. It could be done with Redux for example, but let's take a simpler approach.
Also note that it can't be done by props, because a child component cannot update its parents' props.
Also note that the way you are using the state seems rather strange. It should be set on the class level (or component level).
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {myProperty: 0};
}
}
You could pass a callback method to the Child React Component.
<Child callback={this.onButtonClick} />
On the Client Component, create that callback method:
onButtonClick() {
this.setState({buttonClicked: true});
}
To keep it clean, define the initial value in the constructor. You'll also have to bind the function to have a correct this parameter, otherwise the this variable will be from the event instead of the class you're expecting.
constructor(props) {
super(props);
this.state = {buttonClicked: false};
this.onButtonClick = this.onButtonClick.bind(this);
}
That's it for the Client component.
Now on the Child Component, you'll need to trigger this callback method when possible.
Imagine the Child has the following button, add an event handler on the child component as well, onChildButtonClick. You'll also have to bind in the constructor.
constructor(props) {
super(props);
// bind this for access to this class' data
this.onChildButtonClick = this.onChildButtonClick.bind(this);
}
onChildButtonClick() {
// Might want to do a typeof is function check here too
if (this.props.callback) {
// Trigger the callback on the parent component, letting it know the button was triggered
this.props.callback();
}
}
render() {
return <button onClick={this.onChildButtonClick}>Click me</button>;
}
During initialisation, the Parent component sends a callback method to the child component. Whenever the button is clicked on the child component, the child component triggers the function (callback) given by the parent, essentially running a piece of code on the parent component, which then updates the state with the requested value (could be a string, or anything).
Redux
Redux is another way of doing it, which basically keeps a sort of tracked database that can be used from any component, by pageload - however, that would require an entire tutorial.

React Native NavigatorIOS Child Component Doesn't Update When Parent State Changes

I am trying to get a side menu to open up when I press on a notifications button on the NavigatorIOS React Native component, but the child component doesn't seem to update when the parent's state changes. Thus, pressing on the notifications button is not doing what I want it to do. The parent and child component are as follows. How can I get the child component to update when the parent state changes using NavigatorIOS?
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
rightMenuOpen: false
}
this.rightButtonPress = this.rightButtonPress.bind(this);
}
rightButtonPress() {
this.setState({rightMenuOpen: true});
}
render() {
return (
<NavigatorIOS
initialRoute={{
component: Home,
rightButtonIcon: source=require('./img/notifications.png'),
onRightButtonPress: this.rightButtonPress,
passProps: {index: 1, rightMenuOpen: this.state.rightMenuOpen},
}}
style={{flex: 1}}
/>
)
}
}
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
const menu = <Switch/>
return (
<View style={styles.body}>
<SideMenu
menu={infoText}
menuPosition="right"
disableGestures={true}
isOpen={this.props.rightMenuOpen}
>
<WebView
source={{uri: 'https://www.michigandaily.com/'}}
bounces={false}
/>
</SideMenu>
</View>
);
}
}
The way you're trying this won't work because changes to the value of passProps won't cause a re-render. What you need is a way to pass a signal to the child from the parent using the Event Emitter or a State Management framework.
Example of how to use the event emitter:
https://colinramsay.co.uk/2015/07/04/react-native-eventemitters.html
I also would recommend using a state management framework like Redux or Mobx (I prefer Mobx for its simplicity).
You can learn more about Mobx here: http://mobx.js.org

React Native Navigator: Can I remove navigation gestures from a scene after a user action?

I want to disable the swipe from left pop gesture on the navigator after the side menu has been accessed within a scene. I don't want to disable it when the scene first renders, only when the side menu is open. I have an onOpen function I can call, but I don't know how to programatically change the navigation gestures without pushing another route.
I tried setting the configureScene prop of the navigator like this:
configureScene={() => {
return this.state.swipeBackNavigation ? FloatFromRight : Navigator.SceneConfigs.FloatFromRight;
}
and changing the state, but the component doesn't rerender
ideas would be appreciated
I believe you can just set gestures to null (effectively disabling it):
gestures: {}
I can't test this currently, but I suspect it will work (if I didn't screw up some syntax somewhere):
export default class Foo extends Component {
constructor(props){
super(props);
this.state = {
//initialize gestureChoice
gestureChoice: {},
}
}
disablePop(){
setState({ gestureChoice:{ gestures:{} } });
}
enablePop(){
setState({gestureChoice: ...Navigator.SceneConfigs.FloatFromRight});
}
render(){
return(
<Navigator
renderScene={(route, navigator) =>
return <SomeScene navigator={navigator} {...route.passprops} />
}
configureScene={(route, routeStack) =>
this.state.gestureChoice;
)}
/>
);
}
}
The idea being, you could use enablePop() and disablePop() whenever you would like.
This thread is probably helpful: https://github.com/facebook/react-native/issues/1014

How to reference custom components in react-native?

So I have a custom component like this:
class MyComponent extends React.Component {
render() {
return (
<TouchableHighlight style={this.props.buttonStyle}>
<Text style={styles.buttonText}>Button</Text>
</TouchableHighlight>
);
}
}
And I use the component like this:
class RootView extends React.Component {
render() {
return (
<View>
<MyComponent/>
<MyComponent/>
</View>
);
}
}
The RootView is resizable. What I want to do is shrinking it's children MyComponent when RootView is small enough. And I need to shrink each MyComponent at a time: when RootView is small enough, shrink the first MyComponent, and when the first MyComponent reach a minimal size, shrink the second MyComponent.
I know there's refs in react-native, but it does not seem to work for custom component.
You can add a ref to a custom component:
<Separator ref='sep' style={styles.offsetSeparator} />
So you should be able to do what you need.
However, when you find yourself having a strong dependency between different components, it means that it's probably a good time to start using Flux or something similar and then hold the data in a Store which will hold all the information needed for all 3 components. More about Flux here: https://facebook.github.io/flux/