How can we create a react-native component which can be displayed with .show() method - react-native

I am looking for pointers where I can start from.
I want to create a react native feedback form which can be displayed with .show method.
for eg:
export class FeedbackComponent extends React.component{
show() {
// define this method in a way so that can be called from outside as FeedbackComponent.show()
// which eventually create a new screen with below rendered View
}
render (){
return <View>Feedback Form</View>
}
}
I should be able to use this Component in any other component as
import FeedbackComponent from './FeedbackComponent'
new FeedbackComponent.show()

I would always start with considering the application state. UI in React is updated whenever the State of our components changes.
In your case I would have to think of the parent context in which your feedback form will need to be displayed. In its simplest form, this context will likely be a parent screen-component within which your FeedbackForm component is either shown or hidden.
I've made a Snack of a simple implementation you can find it here: https://snack.expo.io/#stephos_/show-feedback-form
In my case, the App component is the parent screen-component within which we need to render or not render a FeedbackForm Component.
So I would start with adding the relevant state property to the App (parent screen) component like so:
state = {
feedbackFormVisible : false
}
I would then define a method within the same parent class in order to toggle the state when we need to like so:
handleFeedbackFormVisibility = () => this.setState(prevState => ({feedbackFormVisible:!prevState.feedbackFormVisible}))
This handler takes in the previous state in our parent component and toggles the value of the feedbackFormVisible property (i.e. from false to true).
In my case, I call this handler every time we press a Button component like so:
<Button title="Give Feedback" onPress={this.handleFeedbackFormVisibility}/>
You could however trigger the same handler and update the state of the parent component in any other way (i.e. after a timer expires or after a specific scroll point is passed).
The App Component's render method will then decide if the FeedbackForm component should be displayed based on the value of the feedbackFormVisible in our App Component's state. We achieve this by wrapping our FeedbackForm component within an Elvis Conditional within the render method which will return the the appropriate UI (i.e. either with a visible feedback form or not):
{ this.state.feedbackFormVisible ? () : null}
Below the full App component code:
import * as React from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import FeedbackForm from './components/FeedbackForm';
export default class App extends React.Component {
state = {
feedbackFormVisible : false
}
handleFeedbackFormVisibility = () => this.setState(prevState => ({feedbackFormVisible:!prevState.feedbackFormVisible}))
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
This is the App Parent Component
</Text>
{
this.state.feedbackFormVisible ?
(<FeedbackForm />)
: null
}
<Button title="Give Feedback" onPress={this.handleFeedbackFormVisibility}/>
</View>
);
}
}
And below the Feedback Component code (Notice that the shown/hidden logic is actually handled in the parent component not in here):
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
export default class FeedbackForm extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
This is the feedback form!
</Text>
</View>
);
}
}

Related

Adding a function to the passed this.props.onPress-function in a React Native app

I have changed all the TouchableOpacity-components to a custom component so I can add a universal function to all the buttons / clickable views in my application. I call the new component HapticButton.
All the HapticButton-components will contain onPress properties like this:
<HapticButton activeOpacity={1.0} onPress={() => { console.log("button was pressed"); }}>...</HapticButton>
And my HapticButton-class looks like this:
import React from 'react';
import { TouchableOpacity, Text} from 'react-native';
export default class HapticButton extends React.Component {
render() {
return (
<TouchableOpacity activeOpacity={this.props.activeOpacity} style={[this.props.style]} onPress={this.props.onPress}>
{this.props.children}
</TouchableOpacity>
);
}
vibrate() {
// Code that makes a haptic feedback when called
}
};
I succesfully pass on the onPress-property to my new HapticButton-component, but how do I merge together my the this.props.onPress-property with the vibrate()-function so that this gets called every time the HapticButton is pressed?
You can easily merge two function call together as below
<TouchableOpacity onPress={() => {
this.props.onPress();
this.vibrate();
}}>
or you can directly invoke this.props.onPress() in the vibrate function
<TouchableOpacity onPress={this.vibrate} ...
vibrate() {
this.props.onPress();
// Code that makes a haptic feedback when called
}
Not much differences based on your use case between the two ways I've shared, readability wise I think first way is better

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.

Pass touch event to view's parent in react native.

I have a <view> touching which will open a collapsible view. I have used react-native-collapse-view for it (https://www.npmjs.com/package/react-native-collapse-view) I have <text> on top of <view> which is covering it full. I am setting some conditions in onPress event of the <text> element.
Now what I want is if I touch the <text> (obviously I cannot touch view as text is covering it fully), along with onPress event of the <text> the underlaying <view> should also be touched so that it opens the collapsible view.
In short I want to pass the touch event to the parent view in order to complete all work in one touch. I searched and found some content related to onStartShouldSetResponder and pointerEvents but I couldn't get the complete grip as I am a newbie to react native.
in short you need pass function to children for handling events
import React from "react";
class ChildComponent extends React.PureComponent {
render() {
const {handleChildPress} = this.props;
return <Text onPress={handleChildPress}/>;
}
}
class ContainerComponent extends React.PureComponent {
// you need pass this func to children
// =()=> means bind it
handleChildPress=()=>{
console.log('child pressed')
}
render() {
return <ChildComponent handleChildPress={this.handleChildPress}/>;
}
}
export default ContainerComponent

react-native props updated and render called, but UI doesn't updated

I have a react-native component that uses native-base.
I also integrated react-redux and have connected properly, thus my state automatically updated by the reducer.
My problem is, my props.data gets updated properly and render gets called, but my ListItem is not updated at all.. I am not very familiar with this react-native components, did I miss anything?
import React, { Component } from "react";
import {
ListItem,
List
} from "native-base";
import {
Provider,
connect
} from 'react-redux';
class MyPage extends Component {
render() {
const { data } = this.props;
console.log('data: ' + JSON.stringify(data)); // this gets called
2x, 1st time is empty, 2nd time is filled
// 2nd times it gets called, looked something like this: "data:
[{"name": "one"},{"name": "two"}]"
return (
<irrelevant components omitted>
<List
dataArray={data}
renderRow={row =>
<ListItem
button
onPress={() => {console.log("todo: navigation")}}>
<Text>{row.name}</Text>
</ListItem>
}
/>
<irrelevant components omitted>
);
}
}
But, the UI still renders empty even after the second call... any idea?
Thanks in advance!

Change view in Navigator in React Native

I'm new to react native.
I was using NavigatorIOS but it was too limiting so I'm trying to use Navigator. In NavigatorIOS I can change a view by calling this.props.navigator.push() but it doesn't work in Navigator, it seems to be structured differently. How do I change views in using Navigator?
That's the minimal working navigator - it can do much more (see at the end):
You need your main "navigator" view to render Navigator component
In the Navigator you need to specify how you should render scenes for different routes (renderScene property)
In this "renderScene" method you should render View (scene) based on which route is being rendered. Route is a plain javascript object, and by convention scenes can be identified by "id" parameter of the route. For clarity and separation of concerns I usually define each scene as separate named component and use the name of that components as "id", though it's just a convention. It could be whatever (like scene number for example). Make sure you pass navigator as property to all those views in renderScene so that you can navigate further (see below example)
When you want to switch to another view, you in fact push or replace route to that view and navigator takes care about rendering that route as scene and properly animating the scene (though animation set is quite limited) - you can control general animation scheme but also have each scene animating differently (see the official docs for some examples). Navigator keeps stack (or rather array) of routes so you can move freely between those that are already on the stack (by pushing new, popping, replacing etc.)
"Navigator" View:
render: function() {
<Navigator style={styles.navigator}
renderScene={(route, nav) =>
{return this.renderScene(route, nav)}}
/>
},
renderScene: function(route,nav) {
switch (route.id) {
case "SomeComponent":
return <SomeComponent navigator={nav} />
case "SomeOtherComponent:
return <SomeOtherComponent navigator={nav} />
}
}
SomeComponent:
onSomethingClicked: function() {
// this will push the new component on top of me (you can go back)
this.props.navigator.push({id: "SomeOtherComponent"});
}
onSomethingOtherClicked: function() {
// this will replace myself with the other component (no going back)
this.props.navigator.replace({id: "SomeOtherComponent"});
}
More details here https://facebook.github.io/react-native/docs/navigator.html and you can find a lot of examples in Samples project which is part of react-native: https://github.com/facebook/react-native/tree/master/Examples/UIExplorer
I find that Facebook examples are either to simplistic or to complex when demonstrating how the Navigator works. Based on #jarek-potiuk example, I created a simple app that will switch screens back and forth.
In this example I'm using: react-native: 0.36.1
index.android.js
import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';
import Apple from './app/Apple';
import Orange from './app/Orange'
class wyse extends Component {
render() {
return (
<Navigator
initialRoute={{screen: 'Apple'}}
renderScene={(route, nav) => {return this.renderScene(route, nav)}}
/>
)
}
renderScene(route,nav) {
switch (route.screen) {
case "Apple":
return <Apple navigator={nav} />
case "Orange":
return <Orange navigator={nav} />
}
}
}
AppRegistry.registerComponent('wyse', () => wyse);
app/Apple.js
import React, { Component } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Apple extends Component {
render() {
return (
<View>
<Text>Apple</Text>
<TouchableHighlight onPress={this.goOrange.bind(this)}>
<Text>Go to Orange</Text>
</TouchableHighlight>
</View>
)
}
goOrange() {
console.log("go to orange");
this.props.navigator.push({ screen: 'Orange' });
}
}
app/Orange.js
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Orange extends Component {
render() {
return (
<View>
<Text>Orange</Text>
<TouchableHighlight onPress={this.goApple.bind(this)}>
<Text>Go to Apple</Text>
</TouchableHighlight>
</View>
)
}
goApple() {
console.log("go to apple");
this.props.navigator.push({ screen: 'Apple' });
}
}
I was having the same trouble, couldn't find a good example of navigation. I wanted the ability to control views to go to a new screen but also have the ability to go back to the previous screen.
I used the above answer by Andrew Wei and created a new app then copied his code. This works well but the .push will keep on creating new layers over each other (Apple > Orange > Apple > Orange > Apple > Orange etc.). So I used .pop in the Orange.js file under goApple() instead of .push.
This works like a "back" button now, which was what I was looking for, while teaching how to navigate to other pages.