how to show a component depending of picker value selected? [React native] - javascript

I'm trying to make a game with react native and I want to show a different options when i change the picker value.
basically when I select the first option on the picker a component has to appear and when I select the second one another component.
I tried this function but not working
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
here is my code
export default class Facil extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true,
PickerValueHolder : '',
filter: [
{
"option":"Palabras por categoria"
},
{
"option":"Palabras por caracteres"
}
],
dataSource:[]
}
}
componentDidMount() {
return fetch(API_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
const resizeMode = 'stretch';
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
return (
<View style={styles.container}>
<Image source={require('../../Images/HomeLayout.png')}
style={styles.imagen}
/>
<View style={styles.mView}>
<View style={styles.panel}>
<Text style={styles.titlePanel}>MODO FACIL</Text>
<Text style={styles.instructions}>{instructions[0]}</Text>
<View style={styles.picker}>
<Picker
selectedValue={this.state.PickerValueHolder}
style={ {height: '100%',width: '100%'}}
mode="dropdown"
onValueChange={(itemValue, itemIndex) => this.setState({PickerValueHolder: itemValue})} >
{ this.state.filter.map((item, key)=>(
<Picker.Item label={item.option} value={item.option} key={key} />)
)}
</Picker>
</View>
<View style={styles.gameOpt}>
<Text>[dynamic options]</Text>
{pickerOptionText}
</View>
</View>
</View>
<TouchableOpacity style={styles.button}><Text style={styles.btnText}>Play!</Text></TouchableOpacity>
</View>
);
}
}

You forgot '()'.
pickerOptionText is a function, not a React component.
<Text>[dynamic options]</Text>
{pickerOptionText}
to:
<Text>[dynamic options]</Text>
{pickerOptionText()}

You can try using Conditional Rendering of JSX, by this you can use ternary operator and a simple if condition. this is written as:
{this.state.PickerValueHolder==this.state.filter[0] ?
<Text>{instructions[2]}</Text>
:<Text>{instructions[1]}</Text>
}
and if you need simple if condition then,
{ condition == true && <Text>your text here</Text>
}

Related

Changing style of specific component returned from map function onClick

I am trying to change the style of individual TouchableOpacity components that have been returned from a map function.
Here is the component:
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<React.Fragment key={id}>
<TouchableOpacity
style={styles.button}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
Let TouchableOpacity = TO.
The map function returns about 30 TOs with unique IDs. When I click the TOs, I can see their unique ID in the console log. I want to know how I can modify the style of an individual TO.
Here is my render function which uses the functional component Example.
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList() }} />
</View>
</ScrollView>
</View>
);
}
What I have tried:
referencing this stackoverflow post, I tried to create a function which changed the style of the TO when it is clicked. But the result of this changed all the TOs in the UI since of the way it is mapped.
I tried something like the following.
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
let buttonStyle = this.state.pressed ? styles.button : styles.buttonClicked
return (
<React.Fragment key={id}>
<TouchableOpacity
style={buttonStyle}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
But as previously stated, this changed all of the Touchable Opacitys. Is there a way to only change one?
Thanks
Edit - to show entire class
class Page extends Component {
constructor(props) {
super(props)
}
MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
);
}
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id}/>
);
})}
</View>
);
};
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList()}} />
</View>
</ScrollView>
</View>
);
}
}
It is easier to separate the component inside map to a separate component and then handle style changes on press there
const MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
)
}
so you can use in the map like this
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id} />
);
})}
</View>
);
};

How to setState in different component React native?

My data from the api is loaded in a component and returns a view containing the values, which does show in the other component. But I want an activity indicator on that page to show until the data is completely loaded.
I've followed some tutorials about setState with parent-child, child-parent, sibling-sibling and parentless relations. But none seem to work the way I want it.
This is the screen that shows first. It starts with the 'refreshing' view, which will show an activityindicator until the state.refreshing is set to false.
export default class AgendaScreen extends React.Component {
static navigationOptions = {
title: "Agenda"
};
constructor(props) {
super(props);
this.state={
refreshing: true
}
}
render() {
if (this.state.refreshing){
return(
//loading view while data is loading
<ImageBackground source={ScreenBackground} style={styles.container}>
<View style={{flex:1, paddingTop:20}}>
<ActivityIndicator />
</View>
</ImageBackground>
)
}
return(
<ImageBackground source={ScreenBackground} style={styles.container}>
<ScrollView>
<ImageBackground style={[styles.header,{justifyContent:'flex-end'}]} source = {HeaderImage}>
<Text style={{color:'white', fontSize:12, alignSelf:'center', backgroundColor:'transparent', marginBottom:2}}>
Complete summary
</Text>
</ImageBackground>
<Text style={styles.text}>Agenda</Text>
<Dates />
</ScrollView>
</ImageBackground>
);
}
}
This is the dates component, which gives me the view that I call with in the previous code block.
export default class Dates extends React.Component {
constructor(props) {
super(props);
this.state = {
data:[],
fill:[],
refreshing:true,
dates:[],
list:[]
}
}
componentDidMount() {
this.getData();
}
getData() {
fetch("API")
.then((result)=>result.json())
.then((res=>{
this.setState({
data:res,
refreshing:false
});
this.setState({
fill:this.state.data[0]
});
this.getDates();
this.loop();
}))
.catch(error =>{
console.error(error);
});
};
onRefresh() {
//Clear old data
this.setState({
data:[]
});
//Function to call api for latest data
this.getData();
};
getDates() {
var str = t(this.state.test, 'content.rendered').safeString
var arr = str.split(',');
var dates = [];
arr.forEach(function(e) {
dates.push(e.match(/;(\d{0,2}.[a-zA-Z]+)/g)[0].replace(';',''));
});
this.setState({
dates: dates
})
};
tempList=[];
loop(){
for (x=0;x<this.state.data.length;x++)
{
var fill=this.state.data[x]
this.tempList.push(
<View style={{flex:1}}>
<FlatList
data={[fill]}
renderItem={({item})=>
<View>
<Text style={styles.text}>
{t(item, 'title.rendered').safeObject}
</Text>
</View>
}
refreshControl={
<RefreshControl
//refresh control used for pull to refresh
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
/>
}
keyExtractor={(item, index) => index.toString()}
/></View>
)
}
this.setState({
list:this.tempList
})
}
render() {
return(
<View style={{flex:1, borderWidth:10}}><Text></Text>{this.state.list}
</View>
);
}
}
What I need is when Dates succesfully loaded his data from the api and returns the view, that the AgendaScreen state.refreshing will be stated to false.
Add below to your AgendaScreen Component
this.refresHandler = (e) =>{
this.setState({
refreshing:e
})
}
Add below props inside <Dates />
<Dates refresHandler={this.refresHandler}/>
change below code in Dates Component
getData() {
fetch("API")
.then((result)=>result.json())
.then((res=>{
this.setState({
data:res,
refreshing:false
});
this.setState({
fill:this.state.data[0]
});
this.getDates();
this.loop();
}))
.then(() =>{
this.props.refresHandler(this.state.refreshing)
})
.catch(error =>{
console.error(error);
});
}

How to get user id from firebase in react native?

I need your help I'm using firebase for my app. I'm trying to get the users ID not the logged users no all users I have. I want to show their (uid) simply like in an alert for each user. Also, I'm showing them in a flatlist and when I set item.uid in an alert it shows (undefined). But, all the other data of the user is shown correctly. This what I did until now:
**
users.js
**
export default class usersList extends React.Component{
state = {
loading: false,
uid: '',
users: [],
items: []
};
componentDidMount() {
let itemsRef = f.database().ref('users').once('value').then(snapshot => {
var data = snapshot.val();
var items = Object.values(data);
this.setState({items});
console.log(snapshot.val())
});
}
renderItem({item}) {
return (
<View key={index} style={{width: '100%', overflow:'hidden', marginBottom: 5, justifyContent:'space-between', borderBottomWidth:1, borderColor: 'grey'}}>
<View>
<View style={{padding:5, width:'100%', flexDirection: 'row', justifyContent: 'space-between'}}>
<Text>{item.email}</Text>
</View>
</View>
</View>
)
}
render() {
return (
<View style={styles.container}>
<ScrollView>
{
this.state.items.length > 0
? <ItemComponent items={this.state.items} navigation={this.props.navigation} />
: <Text>No stores</Text>
}
</ScrollView>
</View>
);
}
}
//ItemComponent.js
export default class ItemComponent extends Component {
static propTypes = {
items: PropTypes.array.isRequired
};
render() {
return (
<View style={styles.itemsList}>
{this.props.items.map((item, index) => {
return (
<View key={index}>
<TouchableOpacity
onPress={ () => alert(item.uid)}>
<Text style={styles.itemtext}>{item.email}</Text>
</TouchableOpacity>
</View>
)
})}
</View>
);
}
}
firebase.database().ref('user').on('value', (datasnapshot) => {
this.setState({
_id: datasnapshot.key
});`
This solution worked for me
<Text style={styles.yourStyleHere}>UID: {auth.currentUser?.uid} </Text>

How to Fetch APIs and Print first element in response Array?

My React Native app fetch API data and I need to print the first index of response but it's not, and gets all of the "ozone" for example in all child of the parent Array and when I print val[0] when Mapping I have nothing printed
My Code|
export default class App extends Component {
constructor(props) {
super(props);
this.state = { isLoading: true, dataSource: null };
}
async componentDidMount() {
let API_WEATHER =
"https://api.weatherbit.io/v2.0/forecast/daily?city=Raleigh,NC&key={API_KEY}";
fetch(API_WEATHER)
.then(response => response.json())
.then(responseJson => {
console.log(responseJson.data);
this.setState({
isLoading: false,
dataSource: responseJson.data
});
})
.catch(error => {
console.log(error);
});
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator size="large" />
</View>
);
}
let weather= this.state.dataSource.map((val, key) => {
return (
<Text key={key}>
{val.ozone}
</Text>
);
});
return (
<ScrollView style={styles.container}>
<ScrollView>
<View>
<Text>{weather}</Text>
</View>
</ScrollView>
</ScrollView>
);
}
In this part of the code when i log the respone JSON obj
.then(responseJson => {
console.log(responseJson.data);
console.log(responseJson.data[0]);
console.log(responseJson.data[0].datetime);
}
i have what i need, but when print them in View i have Erroe
look at the Images
You're probably the first key of the object.
obj[Object.keys(obj)[0]];
Also, you can use
Try the for … in loop and break after the first iteration
for (var prop in object) {
// object[prop]
break;
}

How to call a child method from the parent in React Native?

When a click event is fired within my parent component I need to call the method closeMenu() from the SearchBar child component. I have tried a couple of different ways to do that but none of them are working. Does anyone know how to do this?
class Products extends Component {
constructor(props) {
super(props);
this.state = { closeMenu: false};
this.hideSearchBar = this.hideSearchBar.bind(this);
}
hideSearchBar(e) {
console.log('e: ', React.Children)
this.setState({closeMenu: true});
this.refs.SearchBar.closeMenu();
this.setState({closeMenu: false});
}
componentWillMount() {
this.props.dispatch(getProductList());
}
render() {
const {isLoading, products} = this.props.products;
if (isLoading) {
return <Loader isVisible={true}/>;
}
return (
<TouchableWithoutFeedback onPress={(e) => this.hideSearchBar(e)} style={{zIndex: 0}}>
<View style={styles.wrapper}>
<Header/>
<View style={styles.bodyWrapper}>
<ScrollView style={styles.scrollView}>
<ProductsContainer data={{productsList: { results: products }}}/>
</ScrollView>
<SearchBar ref="SearchBar" style={styles.searchBar} />
</View>
<Footer/>
</View>
</TouchableWithoutFeedback>
);
}
}
I also tried calling closeMenu() without refs:
hideSearchBar(e) {
this.setState({closeMenu: true});
this.SearchBar.closeMenu();
}
Here is the SearchBar component:
class SearchBar extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.suggestions = [];
}
componentWillUpdate(nextProps, nextState) {
console.log("COMPONENT WILL UPDATE");
console.log(nextProps);
console.log(nextState);
}
suggestionClick = (value) => {
}
getSuggestionText = (suggestion) => {
}
onChangeText = (value) => {
this.selectedSuggestion = false
this.props.dispatch(handleSearchItemText(value));
console.log(this.props.products.searchResults);
}
onFocus() {
const {height} = Dimensions.get('window');
this.setState({
contentOffset: {
x: 0,
y: height * 1 / 3
}
});
}
onBlur() {
this.setState({
contentOffset: {x: 0, y: 0}
});
}
closeMenu = () => {
this.props.products.searchResults = {};
}
componentWillReceiveProps() {
if (!this.props.closeMenu) {
this.props.closeMenu = false;
}
}
renderSearches = () => {
this.suggestions = this.props.products.searchResults;
const suggestionTexts = Object.keys(this.props.products.searchResults || {})
console.log(suggestionTexts);
if (!suggestionTexts.length) {
return null
}
// for android absolute element: https://github.com/facebook/react-native/issues/16951
// https://gist.github.com/tioback/6af21db0685cd3b1de28b84981f31cca#file-input-with-suggestions-L54
return (
<View
ref="suggestionsWrapper"
style={autoStyles.suggestionsWrapper}
>
{
this.suggestions.map((text, index) => (
<TouchableHighlight
key={index}
suggestionText={text}
activeOpacity={0.6}
style={autoStyles.suggestion}
onPress={this.suggestionClick(this.suggestions[text])}
underlayColor='white'
>
<Text style={autoStyles.suggestionText}>
{text}
</Text>
</TouchableHighlight>
))
}
</View>
)
}
render() {
const myIcon = (<Icon name="search" size={30} style={styles.searchIcon}/>);
const slidersIcon = (<Icon name="sliders" size={30} style={styles.slidersIcon}/>);
return (
<TouchableWithoutFeedback style={{zIndex: 0}}>
<View style={[styles.searchBar, this.props.style]}>
<View style={styles.searchContainer}>
<View>
{slidersIcon}
</View>
<View style={styles.search}>
<View style={styles.searchSection}>
{myIcon}
<TextInput
style={styles.input}
placeholder="Search"
placeholderTextColor="rgba(0,0,0,0.7)"
onChangeText={(searchString) => {
this.setState({searchString})
}}
underlineColorAndroid="transparent"
editable={true}
autoCorrect={false}
autoFocus={false}
autoCaptialize={'none'}
autoCorrect={false}
onChangeText={this.onChangeText}
enablesReturnKeyAutomatically={true}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
/>
</View>
</View>
</View>
{this.renderSearches()}
</View>
</TouchableWithoutFeedback>
);
}
}
There are some issues which you should avoid:
Never mutate props: this.props.something = {} is an anti-pattern. Think about props as data that your component does not own and which are not mutable. If they change then only because the parent passed new props.
Also you have multiple handlers in your SeachBar that are not bound to this but use this. It will not work. Use arrow functions if you want to use this or bind them in the constructor.
You should overthink the architecture of your app. Maybe it is a good idea to split the search bar and the result list into two separate components. When the user types something to search for update your redux store and display the results in a separate component that you only render if there are search results.
I'm affraid it would exceed the length of a stackoverflow answer to solve all these issues. Maybe you should go back to the basics first and do the really good redux tutorial.

Categories

Resources