I'm having some troubles understanding the code.
Basically if I do this:
<Text onPress={() => console.log('123')} >123</Text>
,
and I click on the text, it logs me 123 each click.
But I'm doing a dialer app. Basically having a component Tile (representing a single number, also with secondary option (but that will be dealt with later)).
So as I'm including my (currently only single one) Tile in App.js, I want onPress event to call function that changes state of currently dialed number. So if user clicks 3 times on '1', I want the final string to be 111.
The thing is, I can see that the handleNumber() function is called once after running code and never again after clicking on the number, therefore never changing state, not logging anything.
App.js Tile implementation:
const [getNumber, setNumber] = useState();
const handleNumber = (newChar) => {
console.log(newChar);
setNumber(newChar);
}
<Tile onPress={() => handleNumber('1')} style={styles.tile} firstChar={'1'} secondChar={'✉'}/>
Tile.js:
const Tile = (props) => {
return (
<TouchableOpacity onPress={props.onPress()}>
<View>
<Text style={styles.mainChar}>{props.firstChar}</Text>
{props.secondChar ? <Text style={styles.secondChar}>{props.secondChar}</Text> : null}
</View>
</TouchableOpacity>
)
}
One more thing:
As you can see in App.js::handleNumber(),
the implementation is currently wrong, because the final number will always be just the new single number, instead of appending it to the end of string.
But if I wanted to do smth like setNumber(getNumber + newChar), it would give Maximum update depth exceeded error.
The thing here is the way that you're passing the onPress prop to TouchableOpacity. See, you're calling the props.onPress function instead of just passing it to the component, which causes the function to be executed when the component renders (or rerenders).
You should be fine just by using <TouchableOpacity onPress={props.onPress}> and then setNumber(getNumber + newChar) will be fine as well.
As a side note, you could initialize your App.js state with the '1' string that you wish to be your initial value (const [getNumber, setNumber] = useState('1')), and then the Tile component can receive getNumber directly.
Related
Say that I have a component where I create child components based on the number of elements passed from the parent ( think of it as a page indicator, for example ).
What I would like to do, is to animate the transition between these dots (their backgroundColor) depending on which one is active.
I have tried in a few ways to match the current value to the index of the child element, however, since I don't have the index beforehand, my attemps have failed.
I am posting the basic component down there without my fail attemps to animate it so it illustrates better what I am talking about.
How would one go about doing that in reanimated 2?
const component = ({active, amountOfDots}) => {
const dotsArray = Array(amountOfDots).fill(0);
return (
<View style={{}}>
{dotsArray.map(() => {
return <Animated.View style={{}} />;
})}
</View>
);
};
I am trying to create a button that changes the text when clicked and it saves a value to an array. I then need to save the state of the button and the array to the device using AsyncStorage.
I have the first part done (please see Snack link here) but I am struggling to get the AsyncStorage to work properly. If the button had been clicked once, I would expect that the array has the "item 1" value in it and the button would say clicked. Even if the app is closed and reopened, those values should still remain until the button is clicked again.
I have not found any solutions so far. Is there anyone that has some ideas?
This is the workflow that you should follow:
create an object in state (in this.state = {} for classes, or setState() with hooks) for your button's text value
initialize the above value with the value from AsyncStorage (make sure to add some conditional that if it's empty it returns [])
also take note of how Asyncstorage is async, meaning you'll have to add 'await' when you're assigning the value
add some conditional for the text value of your button, whereas it will show a loading icon, (or nothing, doesn't matter) while AsyncStorage is retrieving the initial data
onPress on your button will change the state value AND the AsyncStorage value (or you can only update the AsyncStorage value when you're closing the page with componentWillUnMount, or useEffect(() => return(--do it here--))
If you're using functional components, it would look something like this:
const [textValues, setTextValues] = useState([])
const setInitialValues = async () => {
const info = await AsyncStorage.getItem('myValues')
setTextValues(info)
}
useEffect(() => {
setInitialValues()
return(
AsyncStorage.setItem('myValues', textValues)
)
}, [])
return(
<View>
<Button onPress={() => setTextValues(textValues + 1) title={textValues.length === 0 ? '' : textValues[textValues.length - 1]}}
</View>
)
I'm in the process of creating my own NumberInput component using react-natives TextInput. However I'm having trouble trying to validate the input the user is trying to submit/enter in to the field. I have a regex to check if the value the user is trying to input is a number, if it is we update the state number, if not I dont want the value to update the state. The regex works but when the user tries to enter any other character than an number, say , or ., the input field flickers showing the character but quickly erases it so to say.
I've recorded a gif showing what I mean (https://i.gyazo.com/9f86dbfd7d7cba17c480a7b4c456e004.mp4). Is there any way to prevent the flickering? I am already implementing the onTextChange so I don't see how the value even tries to get updated.
const Container = () => {
const [number, setCurrentNumber] = useState();
const numberRegex = /^\d+$/;
const numberValidation = (updatedValue) => {
if (numberRegex.test(updatedValue)) {
setCurrentNumber(updatedValue);
}
};
return (
<View>
<TextInput
value={number ? number.toString() : undefined}
onChangeText={(value) => numberValidation(value)}
keyboardType="numeric"
/>
</View>
);
};
I am trying to set the state of a component prior to making the API call. The problem is the API call being called first. Here is what I have.
onPress={() => {
setMeal('dinner');
addToLogButtonPressed();
}}
When I press the button addToLogButtonPressed(); calls first which causes an error.
How to I call setMeal before addToLogButtonPressed?
I think you can use useEffect to do that
const [meal, setMeal] = useState('')
useEffect(() => {
addToLogButtonPressed();
}, [meal])
onPress={() => {
setMeal('dinner');
}}
I also face this problem for my previous project simply just pass meal value to your addToLogButtonPressed() and access it inside the function.
If you are not using "dinner" value anywhere else you can skip setting state it will save you one Re-render.
onPress={() => {
setMeal('dinner');
addToLogButtonPressed('dinner'); // like this
}}
I tried all possible ways to get a simple parameter on file MembershipCard.js. My Home component Home.js simply passes props to MembershipList.js where I have done minor Array operations and iterate it to prepare a list. Each item from the list is then pass on to third file MembershipCard.js. I'm getting membership object in this file and able to prepare a card list at Home page. On Home page I have to show a side line whereas I don't want this side line on other pages (which are also accessing MembershipCard.js) hence I'm trying to send a variable on which I will conditionally show side line.
But after so many try out I'm still receiving undefined
This is my React component - Home.js
render () {
return (
<Surface>
<GreetingCard profile={this.props.profile.Profile}/>
<MembershipList props={this.props}/>
</Surface>
)
}
MembershipList.js - this contain only few functions
renderMembershipCard = (membership, i, props, sideLine = true) => {
return (
<TouchableOpacity key={i} style={styles.membership} onPress={() => props.navigation.navigate('Item', { title: membership.gym_name })}>
{/* <MembershipCard {...{membership, sideLine }}/> */}
<MembershipCard {...membership} sideLine={sideLine}/>
</TouchableOpacity>
)
}
const MembershipList = (props) => {
let membership = props.props.profile.Membership
let listArray = [];
Object.keys(membership).forEach(key => listArray.push(this.renderMembershipCard(membership[key], key, props.props)));
return (
<View>
<Text style={styles.ListTitle}>Active Membership ({listArray.length})</Text>
{listArray}
</View>
);
}
MembershipCard.js - this file is part of my presentation layer. It only return a Card design.
const MembershipCard = ({membership,sideLine}) => {
console.log('sideLine', sideLine); // showing undefined
console.log('membership', membership);
return (
<Card>
<Text style={styles.gymTitleText}>{membership.name}</Text>
... JSX code
</Card>
)
Make the following changes to your code and it should work. Change props.props seems to be incorrect way of passing props. Use spread operator for passing all props to children in correct manner.
<MembershipList {...this.props}/>
const MembershipList = (props) => {
let membership = props.profile.Membership
let listArray = [];
Object.keys(membership).forEach(key => listArray.push(this.renderMembershipCard(membership[key], key, props)));
return (
<View>
<Text style={styles.ListTitle}>Active Membership ({listArray.length})</Text>
{listArray}
</View>
)}
<MembershipCard membership={membership} sideLine={sideLine}/>
I solved it using simple trik.
Instead of calling it as a component -
<MembershipCard {...membership} sideLine={sideLine}/>
call it as a simple JS function using curly braces {} -
{ MembershipCard (membership, sideLine) }
This way I can easily pass as many parameters and can easily access all those in called function.