react native don't show loop iteration number when click on button - react-native

sorry for my English language
I wrote a function for push JSX elemnts in an array and return that:
import * as React from 'react';
import { View, Text } from 'react-native';
import { Button} from 'react-native-paper';
const buttonLoop = () => {
const items = []
for (var i = 1; i <= 2; i++) {
items.push(<Button icon="camera" onPress={() => alert(i/* Problem is here*/)}>Loop {i/* It work true*/}</Button>)
}
return items;
}
as you can see i inside of element work truly, and show 1 for Button1 and show 2 for Button2.
But when i click on button it show "3" for both button, I want show "1" for Button1 and "2" for Button2
in JQuery i had't this problem, why it don't work true and what's solution?

Think of it like this,
When you click the button the i you are referring to is pointing to the one in the loop which has the value 3 so the way to fix this is passing the i as a parameter like below
This happens when using var due to closures, you will have to use the let keyword.
const ButtonLoop = () => {
const items = [];
for (let i = 1; i <= 2; i++) {
items.push(
<Button icon="camera" onPress={() => alert(i)} title={i}>
Loop {i}
</Button>
);
}
return items;
};

Related

Console logs only show up once in React Native

I'm trying to get the hang of react native with a very simple app which will log how many times I press a button. Here's my code and my output.
Code:
import React from 'react';
import { View, Text, Button} from 'react-native';
let numOfTimesPressed1 = 0;
let numOfTimesPressed2 = 0;
function printTimesPressed(num) {
if (num == 1) {
numOfTimesPressed1 += 1;
console.log("Log In Button was pressed " + numOfTimesPressed1 + ".");
} else {
numOfTimesPressed2 += 1;
console.log("Log In Button was pressed " + numOfTimesPressed2 + ".");
}
}
class HomeScreen extends React.Component {
render() {
return (
<View>
<Text>Page Master</Text>
<Button
title="Log In"
onPress={printTimesPressed(1)}
>
</Button>
<Button
title="Sign Up"
onPress={printTimesPressed(2)}
>
</Button>
</View>
);
}
}
export default HomeScreen;
Here's my output after I've hit both buttons multiple times:
LOG Running "PageMaster" with {"rootTag":1}
LOG Log In Button was pressed 1.
LOG Log In Button was pressed 1.
Why won't it update?
onPress expects a function to be passed. Your code passes not a function but a result of calling printTimesPressed(1), which is undefined
onPress={printTimesPressed(1)} // wrong: printTimesPressed(1) is not a function but a code that returns nothing
What you want is this
onPress={() => printTimesPressed(1)} // () => printTimesPressed(1) is a function
onPress={function() { printTimesPressed(1) }} // same thing
now onPress receives anonymous arrow function that will, when executed (when button is pressed), call printTimesPressed with respective argument
This happens because functions, on which React is based, are stateless. This is a specialty of functional programming. That means, functions cannot count, and will always act exactly the same when passing the exact same param. You will have to save your number elsewhere (e.g. in a state) to count how often a function is called. Here, you can find an example of a React Counter Button: https://wsvincent.com/react-counter/
First of all,
Please declare the variables and functions in class and try to use latest ECMAScript (ES).
here is the updated code according to standards, the main issue was the onPress handling, as you are trying using an older ES version it was causing issues and lastly in case of one class export that class directly
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class HomeScreen extends React.Component {
numOfTimesPressed1 = 0;
numOfTimesPressed2 = 0;
render() {
return (
<View>
<Button title="Log In" onPress={()=>this.printTimesPressed(1)} />
<Button title="Sign Up" onPress={()=>this.printTimesPressed(2)} />
</View>
);
}
printTimesPressed = (num) => {
if (num == 1) {
this.numOfTimesPressed1 += 1;
console.log('Log In Button was pressed ' + this.numOfTimesPressed1 + '.');
} else if (num == 2) {
this.numOfTimesPressed2 += 1;
console.log('Sign up Button was pressed ' + this.numOfTimesPressed2 + '.');
}
}
}

react native conditional rendering with section list and flatlist

I have two different components one is CategoryList which is a flatlist and regionlist is a section list. I would like to display the CategoryList first and when item clicked the regionlist will show however I m not sure why it is not working. (I use context to store the state)
{!isToggle ? (
<CategoryList></CategoryList>
) : (
<RegionList style={styles.regionListStyle}></RegionList>
)}
I also create a button to see if it is a problem about the context but it is not.
const ToggleContext = createContext(true);
export const useToggle = () => {
return useContext(ToggleContext);
};
export function ToggleProvideData({children}) {
const [isToggle, setToggle] = useState(true)
return <ToggleContext.Provider value={{isToggle,setToggle}}>
{children}
</ToggleContext.Provider>;
}
I just wonder conditional render is it not working for flatlist?
UPDATE: I tried create a state to store the useContext isToggle but it only appears for like 1 sec
I guess isToggle as a boolean variable, hence you can use the below code for rendering conditionally
{ (isToggle === true) &&
<CategoryList></CategoryList>
}
{ (isToggle === false) &&
<RegionList style={styles.regionListStyle}></RegionList>
}

How to get a component from an array and change its props in react native?

I've created some components in a for loop and push them to an array. Can i get any component from this array to change its props -like title ?
fields = [];
for (let i = 0; i < assets.fieldNames[assets.systemLang].length; i++) {
fields.push(
<InfoField
handlePress={() => this.fieldPressed(i)}
key={i}
title={assets.fieldNames[assets.systemLang][i]}
value="" />
);
};
.....
<View style={styles.infoFields}>
{
fields
}
</View>
and i have a function that i need something like this
changeComponentTitle = () => {
fields[indexForComponent].props.title = "new Title"
}
You'll need to make sure that you trigger a re-render in some way after you make the update (perhaps by putting fields in a state variable and calling setState as mentioned in the comments) but you can use cloneElement to essentially update a single prop of an existing element: https://reactjs.org/docs/react-api.html#cloneelement
changeComponentTitle = () => {
fields[indexForComponent] = React.cloneElement(fields[indexForComponent], {title: "new Title"});
}

React Native - How to render text that has breaklines "\n"

I'm trying to find a way to render texts that come from the database with their breaklines "\n".
How can a change the "\n" to the actual breaklines?
You could use this approach.
import React, { Component } from 'react';
import { AppRegistry, ScrollView, Text } from 'react-native';
export default class Main extends Component {
renderText() {
const textFromDB = 'This text\nfrom\ndatabase.'.split('\n');
const rendered = textFromDB.map(x => <Text>{x}</Text>);
return rendered;
}
render() {
return (
<ScrollView>
{this.renderText()}
</ScrollView>
);
}
}
// skip these lines if using Create React Native App
AppRegistry.registerComponent(
'AwesomeProject',
() => Main);
Using Wisnu answer I kinda found out how to do it, because his approach didn't work for me.
function renderTextWithBreakLines(text) {
return text.split(`\n`).map((txt, i) => <Text key={i}>{ txt }{ '\n' }</Text>)
}
And a shorter method would be:
const renderTextWithBreakLines = text => text.split(`\n`).map((txt, i) => <Text key={i}>{ txt }{ '\n' }</Text>);
You can wrap your text inside {} like so
<Text>{`Large Room~\nFully Furnished`}</Text>
Or declare your text as a variable and render like so
const someText = "Large Room\nFully Furnished"
<Text>{someText}</Text>

Can I use multiple or nested elements in the Label of a Picker Item in React Native?

I'm using React Native with NativeBase and would like to make the labels of my Picker more complicated than just one plain string of text.
But is it even possible to pass elements as the label, say multiple child elements wrapped in a single top-level element?
Or do Pickers only support plain text as labels?
As requested by bennygenel, here's a version of what I've tried:
export default class ThingPicker extends React.Component {
render() {
const {
initialThing,
things,
onThingChanged,
} = this.props;
const orderedThings = things.sort();
return (
<Picker
selectedValue={initialThing}
onValueChange={onThingChanged}>
{buildThingItems(orderedThings)}
</Picker>
);
}
}
function buildThingItems(orderedThings) {
let items = orderedThings.map(th => {
const it = th === "BMD" ? (<Text key={th} label={"foo"} value={"bar"}}>Hello</Text>)
: (<Picker.Item key={th} label={th} value={th} />);
return it;
});
}
Yes! It is possible, it just might not look very "right" for React/JSX code. Just create the elements you need and assign them to the label field:
function buildThingItems(orderedThings) {
let items = orderedThings.map(th => {
const it = (<Picker.Item
key={th}
label={currency === "BMD" ? (<Text>Hello</Text>) : th}
value={th} />);
return it;
});
}