StackNavigator this.props.navigation cached - react-native

I am using a FlatList and within it I am doing:
let pressed = () => {
this.props.navigation.navigate('DetailScreen', {item: item});
}
then in render() on the DetailScreen I am doing: this.props.navigation.getParam('item', null)
so the first item I click on in the list, shows the correct item in the DetailScreen, but then if I go back and click on any other item then this.props.navigation.getParam('item', null) always returns the same item. Its like it does not get changed after the first select.
List.js
_renderRow({item}){
let pressed = () => {
this.props.navigation.navigate('Details', {item: item});
}
let actualRowComponent =
<View style={css.list.row}>
<View style={css.list.rowContainer}>
<Text style={css.list.rowSymbol}>{item.sym}</Text>
<Text style={css.list.rowTitle}>{item.title}</Text>
</View>
<Button title={"$"+item.amount}
onPress={pressed}
backgroundColor='#EE7600'
buttonStyle={{borderRadius: 5}}
/>
</View>;
let touchableWrapperIos =
<TouchableHighlight
activeOpacity={0.5}
underlayColor="#fff"
onPress={pressed}
>
{actualRowComponent}
</TouchableHighlight>;
let touchableWrapperAndroid =
<TouchableNativeFeedback
useForeground={true}
background={TouchableNativeFeedback.SelectableBackgroundBorderless()}
onPress={pressed}
>
{actualRowComponent}
</TouchableNativeFeedback>;
if (require('react-native').Platform.OS === 'ios') {
return touchableWrapperIos;
}
else return touchableWrapperAndroid;
}
render(){
return (
<View style={css.baseStyles.baseContainer}>
<View style={{justifyContent: 'center',alignItems: 'center', marginBottom: 40, marginTop: 50}}>
</View>
<SectionWidget title="Items" >
<FlatList
style={css.baseStyles.flatListContainer}
data={this.items}
renderItem={this._renderRow}
keyExtractor={(item,index) => item.id.toString()}
/>
</SectionWidget>
</View>
);
}
Detail.js
render(){
const item = this.props.navigation.getParam('item', null);
console.log(item.title)
return (
<View style={css.baseStyles.container}>
<ScrollView>
<View style={{marginTop: 40, marginBottom: 60, flex: 1, justifyContent: 'center',alignItems: 'center'}}>
<Text style={css.baseStyles.priceText}>${item.amount}</Text>
<Text style={css.baseStyles.subText}> +$0.50 </Text>
</View>
<Divider style={{ backgroundColor: '#ccc', marginLeft: 10, marginRight: 10 }} />
<View style={{marginTop: 60, marginBottom: 30, flex: 1, justifyContent: 'center',alignItems: 'center'}}>
<Text style={css.baseStyles.titleText}>{item.title}</Text>
<Text style={css.baseStyles.descriptionText}>{item.summary}</Text>
</View>
<Divider style={{ backgroundColor: '#ccc', marginLeft: 10, marginRight: 10 }} />
</ScrollView>
<View style={css.baseStyles.footerContainer}>
<Button title='Buy'
backgroundColor='#EE7600'
buttonStyle={{marginTop: 20, marginBottom: 20, borderRadius: 5, width: 150}}
/>
</View>
</View>
);
}

Since I've never used React Navigation before, I was misunderstanding its usage. In fact your problem seems a logic/usage one, which has a simple solution. Your intention is to have multiple Detail screen (I mean just a component, but multiple screen in the stack), not just one with different params. This makes a BIG difference! React Navigation provides an alternative method to navigation.navigate(), which is navigation.push(), that adds a stack to the 'history' of navigation and seems to be exactly what you're looking for.
So, what you have to do is to replace
this.props.navigation.navigate('Details', {item: item});
with
this.props.navigation.push('Details', {item: item});
You can read more (and better explained) about this on the official doc.
Let me know if this fits with your problem.

The issue I found was due to #autobind. If you use #autobind along with react-navigation, you will run into problems. So I removed #autobind and everything is working as expected.

Related

1st index hidden Item not hidding when swiping the another element - swiperFlatList React native

as you can see in the image, I have implemented adding Text inputs dynamically with the button press, everything is working perfectly only the 1st text input hidden item (Remove Button) not hiding when swiping the other text inputs.
const initialState = {
col1: '',
key: 0,
};
const [inputField, setInputField] = useState<Values[]>([initialState]);
<SwipeListView
data={inputField}
renderItem={data => renderItem(data)}
renderHiddenItem={data => renderHiddenItem(data)}
leftOpenValue={55}
rightOpenValue={-100}
disableRightSwipe={true}
ListHeaderComponent={
<View style={[styles.headingContainer]}>
<Text style={[styles.headingText]}>{Props.inputHeading}</Text>
</View>
}
ListFooterComponent={
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.addBtn}
activeOpacity={0.7}
onPress={onPressAddBtn}>
<Text style={styles.BtnText}>Add</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.submitBtn} activeOpacity={0.7}>
<Text style={styles.BtnText}>Submit</Text>
</TouchableOpacity>
</View>
}
style={{height: Dimensions.get('screen').height / 1.3}}
/>
const renderItem = (data: any) => {
return (
<TouchableHighlight key={data.item.key}>
<TextInput
placeholder="Hello"
onChangeText={e => handleChange(data.item.key, 'col1', e)}
value={data.item.col1}
style={[styles.textInput, Props.textInputStyle]}
// {...Props.textInputProps}
/>
</TouchableHighlight>
);
};
const renderHiddenItem = (rowData: any) => {
return (
<View
style={{
justifyContent: 'flex-end',
flexDirection: 'row',
alignItems: 'center',
}}>
<TouchableOpacity
activeOpacity={0.7}
style={{
backgroundColor: 'red',
justifyContent: 'center',
flexDirection: 'row',
width: 90,
height: 45,
alignItems: 'center',
borderRadius: 5,
}}>
<Text style={{color: 'white'}}>Remove</Text>
</TouchableOpacity>
</View>
);
};
but other all element's swipe is working as expected only the first element is not working as expected
found solution by adding keyExtractor={item => item.key.toString()} to swiper flatlist.

React Native Flatlist ListHeaderComponent Doesn't Work

ListHeaderComponent inside flatlist doesn't work. It won't render nothing in header (above flatlist). If I however put some custom component it does work but it doesn't with my renderHeader function. I tried making a custom component with the same view as in renderHeader but when I used that nothing rendered so I don't know
My flatlist along with whole render:
render() {
const { height } = Dimensions.get("window");
return (
<View style={{flex: 1,
backgroundColor: "#EBECF4"}}>
{/* <Text>
Connection Status :{" "}
{this.state.connection_status ? "Connected" : "Disconnected"}
</Text> */}
{/* <Loader loading={this.state.loading} /> */}
<StatusBar backgroundColor="#63003d" barStyle="light-content" />
<SafeAreaView>
<ModernHeader
backgroundColor="#63003d"
height={50}
text="Eleph"
textStyle={{
color: "white",
fontFamily: "Lobster-Regular",
//fontWeight: "bold",
fontSize: 25,
}}
leftIconComponent={
<TouchableOpacity
style={{ marginRight: 0, margin: 0 }}
onPress={() =>
this.props.navigation.dispatch(DrawerActions.openDrawer())
}
>
<FontAwesome5 name="bars" size={22} color="white" />
</TouchableOpacity>
}
rightIconComponent={
<TouchableOpacity
style={{ marginLeft: 0, margin: 0 }}
onPress={() => this.props.navigation.navigate("Profile")}
>
{/* <MaterialCommunityIcons
name="chart-areaspline"
size={24}
color="#639dff"
/> */}
<Foundation name="graph-bar" size={24} color="white" />
{/* <FontAwesome name="bar-chart" size={24} color="white" /> */}
</TouchableOpacity>
}
/>
</SafeAreaView>
<FlatList
//contentInset={{ top: HEADER_HEIGHT }}
//contentOffset={{ y: -HEADER_HEIGHT }}
data={this.state.post}
extraData={this.state.post}
keyExtractor={(item) => item.id.toString()}
style={{
marginHorizontal: 0,
marginVertical: 6,
}}
renderItem={({ item }) => this.renderPost(item)}
ListFooterComponent={this.renderFooter}
ListHeaderComponent={this.renderHeader}
refreshControl={
<RefreshControl
style={{ color: "#639dff" }}
tintColor="#639dff"
titleColor="#fff"
refreshing={this.state.isLoading}
onRefresh={this.onRefresh}
/>
}
initialNumToRender={7}
onEndReachedThreshold={0.1}
showsVerticalScrollIndicator={false}
onEndReached={() => {
if (
!this.onEndReachedCalledDuringMomentum &&
!this.state.isMoreLoading
) {
this.getMore();
}
}}
onMomentumScrollBegin={() => {
this.onEndReachedCalledDuringMomentum = false;
}}
onScroll={(e) => {
scrollY.setValue(e.nativeEvent.contentOffset.y);
}}
></FlatList>
{/* <View style={styles.footer}>
<TouchableOpacity
activeOpacity={0.9}
onPress={this.getMore}
style={styles.loadMoreBtn}
>
<Text style={styles.btnText}>See more moods</Text>
{this.state.loading ? (
<ActivityIndicator color="white" style={{ marginLeft:8 }} />
) : null}
</TouchableOpacity>
</View> */}
</SafeAreaView>
</View>
My renderHeader function:
renderHeader = () => {
return (
<View
style={{
flex: 1,
flexDirection: "row",
backgroundColor: "#ffffff",
//width: "100%",
padding: 11,
alignItems: "center",
position: "absolute",
justifyContent: "center",
//top: 50,
//bottom: 0,
//left: 0,
//elevation: 4,
//right: 0,
//height: 50,
}}
//flexDirection: "row",
//backgroundColor: "#ffffff",
//width: "100%",
//padding: 11,
//alignItems: "center",
>
<TouchableOpacity
onPress={() => this.props.navigation.navigate("Post")}
>
<Text
style={{ alignItems: "center", color: "#C4C6CE" }}
//placeholder={How are you feeling right now ${this.state.user.name}?}
multiline
>{Tell us how are you feeling right now ${this.state.user.name}?}</Text>
</TouchableOpacity>
{/* <View style={styles.divider}></View> */}
</View>
);
}
Why for example renderFooter works??
renderFooter = () => {
if (!this.state.isMoreLoading) return true;
return (
<ActivityIndicator
size="large"
color={"#D83E64"}
style={{ marginBottom: 10 }}
/>
);
};
Use ListHeaderComponentStyle to style the ListHeaderComponent.
I created a simple snack for you, check if this helps. Your code looks okay but not sure about the whole screen design. Here is my snack link.
https://snack.expo.io/#saad-bashar/excited-fudge
It might be related to the other components inside your screen. So first check only with flatlist, remove all other components. Hope you can sort it out :)
The solution is to remove the "absolute" style in the header for flatlist. The question is mine im just answering with different account but this is the solution that worked.

How to use nextFocusDown in React Native

I need to define the next view that gets focus once the button is pressed, however, there is no much information in React Native documentation (https://reactnative.dev/docs/view#nextfocusdown).
So far I have something like this:
const ListHeader = React.memo(({
navigation
}) => {
return (
<>
<View
style={{
width: '100%',
backgroundColor: '#338cf7',
height: 70,
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
}}
focusable={false}
>
<View
style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}
focusable={false}>
<TouchableNativeFeedback
background={VeoHoverBackground}
onPress={() => navigation.navigate('Sidebar')}
>
<View nextFocusDown={next_to_focus_id}>
<CustomIcon
style={{ padding: 10, color: 'white' }} size={22}
name='customize' />
</View>
</TouchableNativeFeedback>
<TouchableNativeFeedback
background={VeoHoverBackground}>
<View focusable={false}>
<Text style={{ color: 'white', marginLeft: 10, fontSize: 18 }}
focusable={false}>VEO Messanger</Text>
</View>
</TouchableNativeFeedback>
</View>
<TouchableNativeFeedback
onPress={() => navigation.navigate('NewMessageView')}
background={VeoHoverBackground}>
<View>
<CustomIcon
style={{ padding: 10, color: 'white' }} size={22}
name='edit-rounded' />
</View>
</TouchableNativeFeedback>
</View>
</>
);
});
How to get the id of the element that has to be focused next? In my case it has to be second TouchableNativeFeedback element.
So I work with React Native so I am not entirely sure if React works the same way, but you have to pass a ref to the nextFocusDown prop. I see you have the id <View nextFocusDown={next_to_focus_id}>but I don't see the where the next_to_focus_id ref is.
So in React Native, for example, I would make a ref with useRef and use the ref prop on TextInput or TouchableOpacity. Here I will use a really simple TextInput example:
```
const emailRef = useRef();
const passwordRef=useRef();
<TextInput
ref={emailRef}
nextFocusDown={passwordRef}>
</TextInput>
<TextInput
ref={passwordRef}>
</TextInput>
```
Hopefully that helps!

Multiple item data. Show the data as clicked in the dynamically modal with react-native

FOOD> PIZZA> clicking on any pizza opens a modal. I want to show the information of the pizza clicked in this modal. I looked at many similar examples but I could not get out of it. So I need help. I add some parts of the code below. At the same time, the code is available in the expo and the link below
snack.expo.io/#ibrahimyolbir/e48b05
accordion
<Accordion
dataArray={this.state.menus}
animation={true}
expanded={true}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
/>
my open modal button in the render content
onPress={this.triggerModal(food)}
_renderContent = (item) => {
return (
<List>
{
item.food.map((food, index) => {
return (
<ListItem style={{
backgroundColor: "#f0f0f5",
padding: 10,
marginLeft: 0,
paddingRight: 10,
fontStyle: "italic",
listStyleType: "none",
flexDirection: "row",
justifyContent: "space-between"
}}>
<TouchableOpacity
onPress={this.triggerModal(food)}
style={{
flexDirection: "row",
justifyContent: "space-between"
}}
>
<Left style={{
maxWidth: 57
}}>
<Thumbnail source={require("../assets/images/left-food-icon.png")} />
</Left>
<Body>
<Text >{food.name}</Text>
<Text note >{food.description}</Text>
</Body>
<Right>
<Text >{food.price} :-</Text>
</Right>
</TouchableOpacity>
</ListItem>
)
}
)
}
</List>
);
}
MODAL
<Modal
style={{ marginTop: 122 }}
isVisible={this.state.display}
visible={this.state.display}
onSwipeComplete={() => this.setState({ isVisible: false })}
onSwipeThreshold={1}
swipeDirection="down"
animationType="slide"
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={{ flex: 1, backgroundColor: "#fff" }}>
<Text>Hello!</Text>
<TouchableOpacity onPress={this.closeDisplay} style={{ marginTop: 40, marginLeft: 150 }}>
<Text> CLOSE MODAL </Text>
</TouchableOpacity>
{/* <Text> {this.state.menus} </Text> */}
</View>
</Modal>
One of the way can be:
1)Create a new state
currentChildData:[],
currentParentData:[],
2)Pass parent id and child index in onPress event
<TouchableOpacity onPress={()=>this.triggerModal(item.id,index)}
3)Using that passed index and parent id,we can extract relevant information about(to be clicked item) from menus state
triggerModal = (ParentIndex,childIndex) => {
this.setState(prevState => {
let ParentData=[...prevState.menus].find(each=>each.id==ParentIndex)
return {
display: true,
currentChildData: ParentData.food[childIndex],
currentParentData: ParentData,
}
});
}
4)Now we have information of(to be clicked item) in our state. We then can use it like (name of item shown below) inside Modal
<Text>{this.state.currentChildData.name}</Text>
Working Code: https://snack.expo.io/By_QVbPMI
U missed the key from mapped array in a ListItem component. Add it as a prop key={Index} .
Also U need item from map as argument in ur onPress, cuz u need to push visited/selected elements to the state. {()=>this.openModal(itemFromMap)}
And then u can manipulate with data like u need.
As I can understood u have array of objects. Object.keys(itemFromMap).map(...
And return data to the needed part of ur modalInnerContainer.

Align all icon to the right of page regardless of start point React Native

I want to be able to align all reply icons to the far right, where the red line is, regardless of where they start.
Edit: added more information to show how recursion is used in the component. Why I try to use some answers that work without recursion, I receive an undesired effect.
This is the code I have in place so far:
class Comment extends Component {
render() {
return(
<View>
<Header
rounded
style={{
backgroundColor: '#ffffff',
position: 'relative',
}}
>
<View style={{flexDirection: 'row', flexWrap: 'wrap', right: '43%', top: '50%'}}>
<Icon name='chevron-left' size={10} color='#006FFF' style={{top: '6%'}}/>
<NativeText
onPress={() => this.props.history.push('/')}
style ={{color: '#006FFF', fontSize: 12, fontFamily: 'Montserrat-Regular'}}
>
Back
</NativeText>
</View>
</Header>
<View
style={{paddingLeft: '2%', paddingTop: '2%'}}
>
<CommentList
options={this.props.location.state.comments}
currentUser={this.props.location.state.currentUser}
history={this.props.history}
reportId={this.props.location.state.reportId}
optionsForBackButton={this.props.location.state.comments}
/>
</View>
</View>
)
}
}
export default withRouter(Comment)
const CommentList = ({options, currentUser, history, reportId, optionsForBackButton}) => {
return (
<View>
{options.map(option => (
<View
style={{flexDirection: 'row'}}
>
<NativeText
style={{fontSize: 12, fontFamily: 'Montserrat-Regular'}}
>
{option.content}
</NativeText>
<View
style={{flex: 1, alignItems: 'flex-end' }}
>
<Icon
name='reply'
size={12}
// onPress={() => {
// setModalVisible(true)
// changeParentId(option._id)
// }}
onPress={() => history.push({pathname: '/create-comment', state: {content: option.content, currentUser: currentUser, reportId: reportId, parentCommentId: option._id, optionsForBackButton: optionsForBackButton}})}
/>
</View>
{
<View
style={{left: '10%'}}
>
<CommentList
options={option.reply}
optionsForBackButton={optionsForBackButton}
history={history}
currentUser={currentUser}
reportId={reportId}
/>
</View>
}
</View>
))}
</View>
)
}
Set your icon containing view's flex value to 1. This should cause it to fill all remaining space.
See the following snack: https://snack.expo.io/#jrdndncn/playful-churros
class Comment extends React.Component {
render() {
return (
<View
style={{
marginVertical: 2,
flexDirection: 'row',
marginLeft: (this.props.indent || 0) * 20,
}}>
<Text>{this.props.text}</Text>
<View style={{ flex: 1, alignItems: 'flex-end' }}>
<View style={{ width: 20, height: 20, backgroundColor: 'red' }} />
</View>
</View>
);
}
}
<Comment text="hello" indent={0} />
<Comment text="hello" indent={1} />
<Comment text="hello" indent={2} />
Basically marginLeft:'auto' will do the trick. just add style to icon as :
<Icon
name='reply'
size={12}
style={{marginLeft:'auto'}}
// onPress={() => {
// setModalVisible(true)
// changeParentId(option._id)
// }}
onPress={() => history.push({pathname: '/create-comment', state: {content: option.content, currentUser: currentUser, reportId: reportId, parentCommentId: option._id, optionsForBackButton: optionsForBackButton}})}
/>
i added marginLeft:'auto' in style it will automatically shown at the right end of the screen.