Get value from component B in component A in react-native - reactjs

I have a component for dropdown (name : JDrop) :
import React, { Component } from 'react';
import { View,Picker,Image } from 'react-native';
export default class JDrop extends Component {
constructor(props,state) {
super(props)
this.state = {
selectedValue:'',
}
}
render() {
const {items,sizeW,bColor} = this.props;
return (
<View style={{flexDirection: 'row',height: 35, width: sizeW, alignItems: 'center',backgroundColor:bColor}}>
<View>
<Image style={{width:20,height:10, margin:5}} source={require('../../assets/Images/arrowb.png')}/>
</View>
<View >
<Picker
mode='dropdown'
style={{height: 35, width: sizeW,backgroundColor:bColor}}
selectedValue={this.state.selectedValue}
onValueChange={(itemValue, itemIndex) => this.setState({selectedValue: itemValue})}
prompt='Select...'
>
{
items.map( (item,ind) => (
<Picker.Item label={item.name} value={item.value} key={ind} />
))
}
</Picker>
</View>
</View>
)
}
}
I use it in App.js like :
<JDrop items={this.state.priority} sizeW={200} bColor={'blue'} ></JDrop>
how can I get selectedValue from JDrop in App.js ?

There are a few ways, but the easiest is to pass a change handler from the parent to the child that can communicate the changes in the child. Like this:
class App extends React.Component {
onChangeJDrop = (val) => {
console.log(val)
}
render() {
return (
<JDrop onChange={this.onChangeJDrop} items={this.state.priority} sizeW={200} bColor={'blue'} ></JDrop>
)
}
}
class JDrop extends React.Component {
onValueChange = (itemValue, itemIndex) => {
this.setState({selectedValue: itemValue})
this.props.onChange(itemValue)
}
render() {
return (
<Picker onValueChange={this.onValueChange} />
)
}
}

You will need to pass in a callback into your JDrop component (named onSelect below):
<JDrop onSelect={this.onSelect} items={this.state.priority} sizeW={200} bColor={'blue'} ></JDrop>
Then in your JDrop component:
onValueChange={(itemValue, itemIndex) => {
this.setState({selectedValue: itemValue})
this.props.onSelect(itemValue)
} }

Related

Is it possible to open a menu based on the component state? Without the MenuTrigger in react-native-popup-menu

How can I open the Menu without the
<MenuTrigger text='Select action' />
// your entry point
import { MenuProvider } from 'react-native-popup-menu';
export const App = () => (
<MenuProvider>
<YourApp />
</MenuProvider>
);
// somewhere in your app
import {
Menu,
MenuOptions,
MenuOption,
MenuTrigger,
} from 'react-native-popup-menu';
export const YourComponent = () => (
<View>
<Text>Hello world!</Text>
<Menu>
<MenuTrigger text='Select action' />
<MenuOptions>
<MenuOption onSelect={() => alert(`Save`)} text='Save' />
<MenuOption onSelect={() => alert(`Delete`)} >
<Text style={{color: 'red'}}>Delete</Text>
</MenuOption>
<MenuOption onSelect={() => alert(`Not called`)} disabled={true} text='Disabled' />
</MenuOptions>
</Menu>
</View>
);
The doc saying that, Menu can by opened by clicking on or by calling context methods.
I want to know how to use the context methods in this functional component.
Just found a wonderful solution here
This is the sample code that react-native-popup-menu provided.
import React, { Component } from 'react';
import { Text } from 'react-native';
import Menu, {
MenuProvider,
MenuOptions,
MenuOption,
MenuTrigger,
} from 'react-native-popup-menu';
export default class ControlledExample extends Component {
constructor(props, ctx) {
super(props, ctx);
this.state = { opened: true };
}
onOptionSelect(value) {
alert(`Selected number: ${value}`);
this.setState({ opened: false });
}
onTriggerPress() {
this.setState({ opened: true });
}
onBackdropPress() {
this.setState({ opened: false });
}
render() {
const { opened } = this.state;
console.log('ControlledExample - opened', opened)
return (
<MenuProvider
style={{flexDirection: 'column', padding: 30}}>
<Text>Hello world!</Text>
<Menu
opened={opened}
onBackdropPress={() => this.onBackdropPress()}
onSelect={value => this.onOptionSelect(value)}>
<MenuTrigger
onPress={() => this.onTriggerPress()}
text='Select option'/>
<MenuOptions>
<MenuOption value={1} text='One' />
<MenuOption value={2}>
<Text style={{color: 'red'}}>Two</Text>
</MenuOption>
<MenuOption value={3} disabled={true} text='Three' />
</MenuOptions>
</Menu>
</MenuProvider>
);
}
}
Hope this will help others.

Communicating between a parent and child in the React Native tree without access to parent's parent component

Let's say I'm trying to make a Radio Group from scratch for a customer (thus, I don't have access to the component they us it in). I have a RadioGroup component and a RadioOption component. From a customer's usage perspective, it would look like this:
class customerComponent(props..) {
render() {
<View>
<RadioGroup {props..}>
<RadioOption {props} />
<RadioOption {props} />
<RadioOption {props} />
</RadioGroup>
</View>
}
}
How would I communicate between the two components? Without access to the component their being called from? For example, if a user selects a new option, how would I tell the RadioGroup?
You can keep the selected option as a state value in the parent component like this:
class CustomerComponent extends Component {
state = {
selectedOption: 0,
};
handleOptionClick = (option) => {
this.setState({ selectedOption: option });
}
render() {
<View>
<RadioGroup {props..} selectedOption={this.state.selectedOption}>
<RadioOption onClick={this.handleOptionClick} {props} />
<RadioOption onClick={this.handleOptionClick} {props} />
<RadioOption onClick={this.handleOptionClick} {props} />
</RadioGroup>
</View>
}
}
The higher order component would need to provide the options, as well as the callback to be ran when a selection is made. Here is a snack example of a simple radio group that may help you get started https://snack.expo.io/HJA88p3bL
And the code for that snack
import * as React from 'react';
import { Text, TouchableOpacity, View, StyleSheet } from 'react-native';
export default class App extends React.Component {
state = {
selected: []
}
onSelect = (x) => {
this.setState({
selected: this.state.selected.includes(x)
? [
...this.state.selected.slice(0, this.state.selected.indexOf(x)),
...this.state.selected.slice(this.state.selected.indexOf(x) + 1)
]
: [...this.state.selected, x]})
}
render() {
return (
<View style={styles.container}>
<RadioGroup options={["One", "Two", "Three"]} selected={this.state.selected} onSelect={this.onSelect} />
</View>
);
}
}
class RadioGroup extends React.Component {
render() {
return <View>
{this.props.options.map(x =>
<TouchableOpacity style={styles.row} onPress={() => this.props.onSelect(x)}>
<Text>{x}</Text>
<Text>{this.props.selected.includes(x) ? "X" : "O"}</Text>
</TouchableOpacity>)}
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between'
}
});

React native function as prop in flatlist item

Hello I have a problem with calling a function passed as prop at the child component. Im trying to replicate my code with only the relevant lines of code:
class Parent extends Component {
constructor(props) {
super(props)
this.press = this.press.bind(this)
}
press(param) {
console.log(param)
}
renderItem = ({item}) => (
<Child item={item} press={this.press} />
)
render() {
return (
<FlatList renderItem={this.renderItem} />
)
}
}
class Child extends PureComponent {
handlePress(param) {
// do some stuff
// call parent function
this.props.press(param)
}
render() {
const { id } = item
return <Button onPress={() => this.handlePress(id)} />
}
}
At the moment when pressing the button nothing happens, I got this already working with something like this:
<Child press={(param) => this.press(param)} />
however this causes performance issues.
How can I make this work ?
After a while of testing I came to this solution:
class Parent extends Component {
press = (param) => {
console.log(param)
}
renderItem = ({item}) => (
<Child item={item} press={this.press} />
)
render() {
return (
<FlatList renderItem={this.renderItem} />
)
}
}
class Child extends Component {
handlePress(param) {
// do some stuff
// call parent function
this.props.press(param)
}
render() {
const { id } = item
return <Button onPress={() => this.handlePress(id)} />
}
}
It can be a good solution: instead of passing reference from button to parent class, remove the button from child class and use TouchableOpacity instead.
import {
TouchableOpacity,
View,
} from 'react-native'
class Parent extends Component {
constructor(props) {
super(props)
}
press(param) {
console.log(param)
}
renderItem = ({item}) => (
<TouchableOpacity onPress={()=>{this.press(item.id)}}>
<Child item={item} />
</TouchableOpacity>
)
render() {
return (
<FlatList renderItem={this.renderItem} />
)
}
}
class Child extends PureComponent {
render() {
// just your view content
const { id } = item
return <View />
}
}

Why is my custom component not rendering in React Native?

I have created a component called OrderGuideSelect and I am trying to render it in another area of our app. The problem is the OrderGuideSelect component is not rendering. When I set up breakpoints I am able to hit inside of the renderOrderGuideOptions function but it never makes it into the OrderGuideSelect.js file. I also tried putting 'export default' in front of the class declaration instead of the connection but it didn't make a difference. Does anyone know how to get the OrderGuideSelect component rendering properly?
Here is where I call the function that renders the OrderGuideSelect component:
<TouchableOpacity onPress={() => this.renderOrderGuideOptions()}>
<MBIcon name="ico-24-filter" size={30} style={styles.filterIcon}/>
</TouchableOpacity>
And here is the rendering function:
renderOrderGuideOptions = () => {
return (
<View>
<OrderGuideSelect />
</View>
)
}
Here is the OrderGuideSelect.js file:
import React, {Component} from 'react';
import {View, FlatList, ActivityIndicator, StyleSheet} from 'react-native';
import {connect} from 'react-redux';
import {fetchOrderGuides} from '../../actions/AppActions';
import {orderGuideSelected} from '../../actions/ProductAction';
import Header from '../../components/Header/Header';
import {createIconSetFromIcoMoon} from 'react-native-vector-icons';
import selection from '../../selection';
import OrderGuideOption from './OrderGuideOption';
const MBIcon = createIconSetFromIcoMoon(selection);
class OrderGuideSelect extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.dispatch(fetchOrderGuides());
}
selectOrderGuide = id => {
this.props.dispatch(orderGuideSelected(id));
}
render() {
const {isLoading, orderGuides} = this.props.orderGuide;
return (
<View style={styles.wrapper}>
<Header />
<View style={styles.iconLine}>
<MBIcon name='ico-24-filter' style={styles.filterIcon} />
</View>
{isLoading &&
<ActivityIndicator
style={{alignSelf: 'center'}}
animating={true}
size='large'
/>
}
{!isLoading &&
<View style={styles.optionList}>
<FlatList
style={styles.optionList}
data={orderGuides}
keyExtractor={(item, index) => item.id.toString()}
renderItem={({item}) => <OrderGuideOption guideData={item} isSelected={item.id == this.props.selectedGuide.id} onSelected={this.selectOrderGuide} />}
/>
</View>
}
</View>
);
}
}
function mapStateToProps(state){
const {products, orderGuide} = state;
return {
selectedGuide: products.selectedOrderGuide,
orderGuide
}
}
export default connect(mapStateToProps)(OrderGuideSelect);
Also, I may be importing of the OrderGuideSelect component should be correct:
In your code calling this.renderOrderGuideOptions function on onPress event doesn't make sense, i.e. this.renderOrderGuideOptions returns the element but where to append it in DOM?
This should be achived using state in React. So you can set the state in onPress handler then use that state in render to show your OrderGuideOptions component.
So on onPress event bind the function handler:
<TouchableOpacity onPress={this.showOrderGuideOptions}>
<MBIcon name="ico-24-filter" size={30} style={styles.filterIcon}/>
</TouchableOpacity>
Now this showOrderGuideOptions will set the state named showOrderGuideFunction to true.
showOrderGuideOptions(){
this.setState({showOrderGuideFunction: true});
}
At last step use this showOrderGuideFunction state to render your component in the render function like this:
render() {
return (
<div>
...
{
this.state.showOrderGuideFunction &&
renderOrderGuideOptions()
}
</div>
)
}
You can do what you want probably holding a state property in your component and show your OrderGuideOptions according to this state property.
state = { showOrderGuideOptions: false };
renderOrderGuideOptions = () =>
this.setState( prevState => ( { showOrderGuideOptions: !prevState.showOrderGuideOptions }) );
render() {
return (
<View>
<TouchableOpacity onPress={this.renderOrderGuideOptions}>
<MBIcon name="ico-24-filter" size={30} style={styles.filterIcon}/>
</TouchableOpacity>
{ this.state.showOrderGuideOptions && <OrderGuideSelect /> }
</View>
)
}
I think you wanted to something similar to this
class RenderOrderGuideSelectComponent extends Component {
constructor(props) {
super(props);
this.state={
showOrderGuideSelect : false
};
}
renderOrderGuideOptions = () => {
this.setState({showOrderGuideSelect: true});
}
render() {
if(this.state.showOrderGuideSelect) {
return (
);
} else {
return (
this.renderOrderGuideOptions()}>
);
}
}
}

React-native props.navigation.navigate undefined

I really can't get my navigation to work. My navigation works in DrawerNavigator menu but it does not work in components. I've tried everything but there must be something I still do not understand.
I always get an error: "TypeError: undefined is not an object (evaluating'_this.props.navigation')"
This is my View:
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../../store/configure-store';
import StoresListContainer from '../../packages/stores/containers/list-container';
const store = configureStore({});
const Stores = () => {
return (
<Provider store={store}>
<StoresListContainer navigation={this.props.navigation} />
</Provider>
);
};
export default Stores;
This is my Component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
View,
Text,
ActivityIndicator,
FlatList,
Image,
TouchableNativeFeedback,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Styles from './styles';
import Theme from '../../../config/theme';
type Props = {
error: boolean,
loading: boolean,
data: Object,
fetchData: Function,
};
class ListComponent extends Component<Props> {
constructor(props) {
super(props);
this.goToPage = this.goToPage.bind(this);
}
componentDidMount() {
this.props.fetchData();
}
goToPage(param) {
this.props.navigation.navigate(param);
}
render() {
const hasData = Object.keys(this.props.data).length;
const errorMessage = () => {
return (
<Text>Ops! Ocorreu um erro ao carregar os dados.</Text>
);
};
const renderData = (item) => {
return (
<TouchableNativeFeedback contact={item} onPress={() => this.goToPage('Store')}>
<View style={Styles.renderData}>
<View style={{ flex: 0 }}>
<Image style={Styles.renderDataPicture} source={{ uri: item.item.image }} />
</View>
<View style={{ flex: 2 }}>
<Text style={Styles.renderDataTitle}>{ item.item.fantasy_name }</Text>
<Text style={Styles.renderDataSubtitle}>{ item.item.location }</Text>
</View>
<View style={Styles.renderDataItemIconContainer}>
<Icon name="chevron-right" style={Styles.renderDataItemIcon} />
</View>
</View>
</TouchableNativeFeedback>
);
};
const renderLoading = () => {
return (
<View style={Styles.renderLoading}>
<ActivityIndicator color={Theme.color.secondary} size="large" />
<Text style={Theme.text}>Aguarde</Text>
</View>
);
};
const getData = (data) => {
return (
<FlatList data={data} renderItem={renderData} keyExtractor={(item, index) => index} />
);
};
return (
<View style={Styles.container}>
{ this.props.loading ? renderLoading() : null }
{ this.props.error ? errorMessage() : null }
{ hasData ? getData(this.props.data) : null }
</View>
);
}
}
export default ListComponent;
You are using a functional stateless component instead of a class, therefore you need to drop the this.
const Stores = (props) => {
return (
<Provider store={store}>
<StoresListContainer navigation={props.navigation} />
</Provider>
);
};
// ES6 Class
class Stores extends Component {
render(){
return (
<Provider store={store}>
<StoresListContainer navigation={this.props.navigation} />
</Provider>
);
}
}

Resources