React-Native: Adding a Button on press of a Button - react-native

A simple question I can't wrap my head around is this:
Let's say I have a button, if I press that button, I want another button to appear. Imagine that buttoncreation as something that loops, so I can't have a predefined list of buttons I just show or hide.
React can create with react.createElement(), but I don't seem to get the correct usage of createElement.
Can I steer around having to create Elements through react? (Kind of feels like against the nature of react to actually create new html into something)
Easier said: I'm currently developing an app that calculates a common route between multiple departures and destinations, I want to give the user the ability to enter as many departures and destinations as he'd like, since the backend algorithm can handle it.

You can try something like this:
state = {
buttons: []
}
createButton() {
let { buttons } = this.state
const button = (
<Button
onPress={this.createButton.bind(this)}
/>
)
buttons.push(button)
this.setState({
buttons
})
}
renderItem({ item }) {
return (
<View>
{item}
</View>
)
}
render() {
return() {
<FlatList
data={this.state.buttons}
renderItem={(item) => this.renderItem()}
/>
}
}

Related

PagerView works on swipe but not programmatically with setPage

I am working with react-native-pager-view and I am having trouble setting the page programmatically using nativeEvent.setPage()
I have tried a lot of things, went through all the issues regarding it but no luck.
So far I've tried:
Changing the children to simple views exactly like the examples
Hard set the value in a useEffect and call it constantly
Use a separate value to keep track of current page
The swipe gesture is working fine on it and the pages transition, but if I press the button to go forward it doesn't.
If anyone has dealt with this component before please let me know if I'm missing anything.
Code that I'm trying to run. I've changed the View to simple react native view but it didn't work.
//Outside the component
const AnimatedPager = Animated.createAnimatedComponent(PagerView)
const [activeScreenKey, setActiveScreenKey] = useState(0)
const pagerViewRef = useRef<PagerView>(null)
// This function to sync the activeScreenKey if user swipes instead of clicking button
const getActiveKey = (e: PagerViewOnPageScrollEvent) => {
const position = e?.nativeEvent?.position
setActiveScreenKey(position)
}
// This calls the `setPage` and also puts it in a state var that I'm tracking
const setPage = (screenKey: number) => {
pagerViewRef.current?.setPage(screenKey)
setActiveScreenKey(screenKey)
}
<LinearGradient colors={['#3936BD', '#8448DB']} tw="flex-1">
<AnimatedPager
ref={pagerViewRef}
initialPage={0}
scrollEnabled={false}
onPageSelected={getActiveKey}
tw="flex-1"
>
<CustomView
key="1"
title={'title'}
subtitle={'subtitle'}
buttonTitle={'Next ➡️'}
onPress={() => setPage(1)}
/>
<CustomView
key="2"
title={'title'}
subtitle={'subtitle'}
buttonTitle={'Next ➡️'}
onPress={() => setPage(2)}
/>
</AnimatedPager>
</LinearGradient>

React Native doesn't re-render on DOM change

I had an array of components inside a ScrollView component. Somehow react native doesn't re-render when the array is modified.
Here's a demonstration of my problem:
const TestApp = () => {
const [arr, setArr] = useState([]);
function pushArr() {
setArr((arr) => {
arr.push(1);
return arr;
});
console.log('pushArr():', arr);
}
function flushArr() {
setArr([]);
console.log('flushArr():', arr);
}
useEffect(() => {
console.log('useEffect():' , arr);
})
return (
<>
<ScrollView style={{flex:1}}>
{arr.map((elem, i) => <Text key={i}>{elem}</Text>)}
</ScrollView>
<Button title="Push" onPress={pushArr}></Button>
<Button title="Flush" onPress={flushArr}></Button>
</>
)
}
The page remains blank, and no updates happen on button press.
I've logged out arr and these are my findings:
pushArr() and flushArr() works as expected
useEffect() gets triggered only on startup and after flushArr()
Can anyone explain this behavior, and what mistakes have I made?
If I remember correctly, you need to make a copy of the array whenever you want it to “react”. The new memory address will let react know it should update. In other words, you shouldn’t mutate the array.
You can use the spread operator to make a copy and then push an element to the end which you can then pass to useArr. Usually I see people just passing the new object inside your useArr function.
I also don’t see you passing anything to your useArr function.

Programmatically Closing React Native Swipe List View

I'm using react-native-swipe-list-view (https://github.com/jemise111/react-native-swipe-list-view) in my React Native app and I'm trying to close rows programmatically.
Once the user swipes to the right or left and clicks the button there, I call a function to make API calls and handle a few things. This is where I also try to close the row programmatically. This is working ONLY for the last item in the list. Anything above it, the row stays open.
I do hit my handleClickUpdateItemStatus() function and the API call works fine but as I said, only the last item in the list will close. Anything above that one stays open even though all the other code in the function work fine.
My code looks like this:
class MyComponent extends Component {
constructor(props) {
super(props);
this.handleClickUpdateItemStatus = this.handleClickUpdateItemStatus.bind(this);
}
handleClickUpdateItemStatus(itemId, value) {
this._swipeListView.safeCloseOpenRow();
// Call my API to update item status
}
render() {
return(
<Container>
<SwipeListView
ref={ref => this._swipeListView = ref}
data={this.props.items}
... // Omitted for brevity />
</Container>
);
}
}
Any idea what's causing this?

Simplified style change onPress React Native

The following is a first attempt at learning to simply change the style of an element onPress in react native. Being well versed in web languages I am finding it difficult as it is not as straight forward.
For reasons as yet unknown, the element requires two clicks in order to execute.
export class NavTabItem extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
}
this.NavTabAction = this.NavTabAction.bind(this)
}
NavTabAction = (elem) => {
elem.setState({active: !elem.state.active})
}
render() {
return (
<TouchableOpacity
style={this.state.active ? styles.NavTabItemSelected : styles.NavTabItem}
onPress={()=> {
this.NavTabAction(this)
}}>
<View style={styles.NavTabIcon} />
<Text style={styles.NavTabLabel}>{this.props.children}</Text>
</TouchableOpacity>
);
}
}
Other issues:
I also have not worked out how a means of setting the active state to false for other elements under the parent on click.
Additionally, Is there a simple way to affect the style of child elements like with the web. At the moment I cannot see a means of a parent style affecting a child element through selectors like you can with CSS
eg. a stylesheet that read NavTabItemSelected Text :{ // active style for <Text> }
Instead of calling elem.setState or elem.state, it should be this.setState and elem.state.
NavTabAction = (elem) => {
this.setState(prev => ({...prev, active: !prev.active}))
}
And instead of passing this in the onPress, you should just pass the function's reference.
onPress={this.NavTabAction}>
You should also remove this line because you are using arrow function
// no need to bind when using arrow functions
this.NavTabAction = this.NavTabAction.bind(this)
Additionally, Is there a simple way to affect the style of child elements like with the web
You could check styled-component, but I think that feature don't exists yet for react native. What you should do is pass props down to child components.
Thanks to everyone for their help with this and sorting out some other bits and pieces with the code.
The issue in question however was that the style was changing on the second click. A few hours later and I have a cause and a solution for anyone suffering from this. Should any of the far more experienced people who have answered this question believe this answer is incorrect or they have a better one, please post it but for now here is the only way I have found to fix it.
The cause:
Using setState was correctly re rendering the variables. This could both be seen in the console via console.log() and directly outputted in the render making them visible.
However, no matter what was tried, this did not update the style. Whether it was a style name from the Stylesheet or inline styles, they would update on the second click rather than the first but still to the parameters of the first. So if the first click should make a button turn from red to green, it would not do so even though the new state had rendered. However if a subsequent click should have turned the button back to red then the button would now go green (like it should have for the first click). It would then go red on the third click seemingly always one step behind the status passed to it.
Solution
To fix this, take the style off the the primary element (forgive terminology, someone edit), in my case, the TouchableOpacity element. Add in a child View element and place the styles on that View element instead along with the ternary operator and wallah.
It seems any change to status on the effective master element or container if you prefer, only takes affect after another render, not that contained in setStatus.
Final code:
export class NavTabItem extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
}
}
NavTabAction = () => {
this.setState({active: !this.state.active})
}
render() {
this.state.active == true ? console.log("selected") : console.log("unselected")
return (
<TouchableOpacity onPress={this.NavTabAction}>
// added View containing style and ternary operator
<View style={this.state.active == true ? styles.NavTabItemSelected : styles.NavTabItem}>
<View style={styles.NavTabIcon} />
<TextCap11 style={styles.NavTabLabel}>{this.props.children}</TextCap11>
</View>
// End added view
</TouchableOpacity>
);
}
}

React Native StackNavigator Title Change Causes Recursion

I've split out the navigationOptions to the top of each screen to make things easy to understand, rather than keeping it in the TabNavigator. Here's an example of one screen:
static navigationOptions = ({navigation}) => {
const {params = {}} = navigation.state;
return {
title: params.title != undefined ? `${params.title}` : "Step 1: Select a Letter",
tabBarOnPress: ({...props, scene})=>{
params.onFocus();
},
titleStyle: {
textAlign: 'center'
}
}
};
Once a user selects a letter on this screen, I switch the view to show a different component (a list of items that start with that letter, for example).
I'd like to change the title of the TabNavigator Header to now show something like "Step 2: Select an item from the list". I saw on a thread that you can call a method from the method that rebuilds the view like this:
_letterSelectedHandler = (letter) => {
this._changeTitle("Step 2: Select an item...");
...
}
With the method:
_changeTitle = (title) => {
this.props.navigation.setParams({title});
}
Unfortunately, it seems to create an infinite loop. Is there a better way of simply changing the TabNavigator Header Title?
I should mention that I'm not using Redux or anything too complex, since I'm new to React Native. Please let me know if I can provide any more information to help illustrate the issue I'm having.