React native shared elements won't work with react navigation 4 - javascript

I'm trying to implement react-navigation-shared-elements with react-navigation v4, but somthing wrong with the way I'm trying.
I have Screen , that contain component ( SectionList) that rendering and other component ( TaskCard ) that contain the wanted shared element.
I've tried to implement this according to the documentation, but it's not working.
Please see the code:
My Router:
const HomeStack = createSharedElementStackNavigator({
HomeScreen,
TaskDetailsScreen,
EditTaskScreen,
}, {
defaultNavigationOptions: headerDefaultOption,
});
const AppNavigator = createSwitchNavigator(
{
SplashScreen,
AuthStack,
HomeStack,
}
);
HomeScreen that contain the SectionList component:
export const HomeScreen = ({navigation}) => {
const onTaskPressHandler = (task) => {
navigation.push(screens.TASK_DETAILS_SCREEN, {task});
};
return (
<View style={styles.container}>
<TaskList data={tasks} onTaskPress={onTaskPressHandler} onTaskLongPress={openQuickActionsWithData}/>
</View>
);
};
TaskList:
export const TaskList = ({data, onTaskPress, onTaskLongPress}) => {
data.sort((a, b) => (a.taskEndDate > b.taskEndDate) ? 1 : ((b.taskEndDate > a.taskEndDate) ? -1 : 0));
return (
<SectionList
showsVerticalScrollIndicator={false}
sections={sortedData}
keyExtractor={(item, index) => item + index}
renderItem={({item, index}) => {
return (
<TaskCard data={item} index={index} onTaskPress={onTaskPress} onTaskLongPress={onTaskLongPress}/>
);
}}
renderSectionHeader={({section: {title}, section: {data}}) => {
return (
renderTitleIfHasData(data, title)
);
}}
/>
);
};
TaskCard with shared element :
import {SharedElement} from 'react-native-shared-element';
export const TaskCard = ({data, index, onTaskPress, onTaskLongPress}) => {
const onPressHandler = data => {
onTaskPress(data)
};
return (
<Animated.View rkey={index} style={[styles.mainContainer, {transform: [{scale: scaleAnim}]}]}>
<TouchableOpacity
index={index}
activeOpacity={layout.activeOpacity}
style={[styles.taskContainer, {backgroundColor: isFinished ? color.ORANGE : color.GREY,}, renderBorderRadiusPosition()]}
onPress={() => onPressHandler(data)}
onLongPress={() => onTaskLongPress(data)}>
<View style={styles.titleContainer}>
<SharedElement id={`data.${data.taskCreationDate}.sharedTask`}>
<View style={styles.taskImageTypeContainer}>
<Image source={getTaskImageByType(data.taskType)} style={styles.typeImageStyle}/>
</View>
</SharedElement>
</TouchableOpacity>
</Animated.View>
);
};
Details screen with same shared element :
export const TaskDetailsScreen = ({navigation}) => {
return (
<ScrollView style={styles.mainContainer}>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<SharedElement id={`data.${task.taskCreationDate}.sharedTask`}>
<View style={styles.taskTypeContainer}>
<Image source={getTaskImageByType(task.taskType)} style={styles.taskTypeImage}/>
</View>
</SharedElement>
</View>
</ScrollView>
);
};
TaskDetailsScreen.sharedElements = (navigation, otherNavigation, showing) => {
const task = navigation.getParam('task');
return [{
id: `data.${task.taskCreationDate}.sharedTask`,
animation: 'move',
resize: 'clip'
}];
};

Related

Accordion inside a Flatlist React native

I have an accordion inside a flatlist.
Here is the code i have :
const OptionList = (groupOption) => {
return (
<FlatList
data={groupOption.options}
keyExtractor={(result) => result.id.toString()}
renderItem={({ item, index }) => {
const clickedRadio = () => {
const selectedOption = { [item.question]: { ...item } };
setAnswers({ ...answers, ...selectedOption });
};
const status = isOptionSelected(item) ? true : false;
return (
<View key={index}>
<Radio
initialValue={status}
label={item.description}
onChange={() => clickedRadio()}
color="error"
/>
</View>
);
}}
/>
);
};
return (
<View style={styles.container}>
<Text style={{ fontWeight: "bold", fontSize: 16, color:"#6B24AA" }}>
{t("Choose an option/Scroll for more questions")}
</Text>
<FlatList
data={questions}
listKey={(item) => item.id.toString()}
keyExtractor={(result) => result.id.toString()}
renderItem={({ item, index }) => {
const data = [
{
title: item.description,
content: (<><OptionList options=
{item?.question_options}></OptionList></>)
}
];
const status = isOptionSelected(item) ? true : false;
return (
<View style={styles.groupOptions} key={index}>
<Accordion style={styles.accordion}
headerStyle=
{styles.headerStyle} contentStyle={styles.contentStyle}
dataArray={data}
icon={status ? <Icon name="circle"
family="Entypo" size={20} style={{marginLeft: -6,
color: "#6B24AA"}}/>
:
<Icon name="done"
family="MaterialIcons" size={20}
style={{marginLeft: -6}}/>}
expandedIcon={<Icon name="circle"
family="Entypo"/>}
opened={1}/>
</View>
);
}}
/>
</View>
);
The accordion content its anther flatlist component. It shows this error every time i click the accordion.
It shows this error :
VirtualizedList: Encountered an error while measuring a list's offset from its containing VirtualizedList.
at node_modules/react-native/Libraries/Lists/VirtualizedList.js:1411:10 in _scrollRef.measureLayout$argument_2
How can i fix this error? Is it the problem the other flatlist at the content of accordion
Please replace the OptionList component with the given below code.
OptionList
const OptionList = (groupOption) => {
return (
groupOption.map((item,index) => {
const clickedRadio = () => {
const selectedOption = { [item.question]: { ...item } };
setAnswers({ ...answers, ...selectedOption });
};
const status = isOptionSelected(item) ? true : false;
return (
<View key={index}>
<Radio
initialValue={status}
label={item.description}
onChange={clickedRadio}
color="error"
/>
</View>
)
})
);
};
please check and let me know , cheers !

Cannot read property of 'navigate' of undefined

I am trying to make a stack navigator using reactstack navigation. When the button clicks, it appears on the detail screen only, the title of the page is detail. I am not yet to parsing data array to the next screen, it just tries to navigate the screen into the detail screen, and gets this error. I am new to react. Please help me solve this problem.
import React from 'react'
import {Button, StyleSheet, ScrollView, Text, View} from 'react-native'
import {useState, useEffect} from 'react'
import axios from 'axios'
const Item = ({id, user_id, title, onPress, navigation}) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Id :{id}
</Text>
<Text style={styles.text}>User Id :{user_id}
</Text>
<Text style={styles.text}>Tittle :{title}
</Text>
<View style={styles.container}>
<Button onPress={() => navigation.navigate('Detail')} title='Detail'></Button>
</View>
<View style={styles.line}></View>
</View>
)
}
const Berita = () => {
const [users,
setUsers] = useState([]);
useEffect(() => {
getData();
}, []);
const selectItem = (item) => {
console.log('Selected item: ', item)
}
const getData = () => {
axios
.get('https://gorest.co.in/public/v1/posts')
.then(res => {
console.log('res: ', res);
setUsers(res.data.data);
})
}
return (
<ScrollView style={styles.container}>
{users.map(user => {
return <Item key={user.id} id={user.id} user_id={user.user_id} title={user.title}/>
})}
</ScrollView>
)
}
export default Berita
const styles = StyleSheet.create({
container: {
padding: 15
},
text: {
color: "black",
marginTop: 5,
fontStyle: 'italic',
fontSize: 18,
fontFamily: 'Arial'
},
line: {
height: 1,
backgroundColor: 'black',
marginVertical: 20
},
title: {
fontSize: 25,
fontWeight: 'bold',
textAlign: 'center',
color: "black"
},
tombol: {
padding: 10
}
})
This is the stack screen navigator code
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();
const DetailBerita = () => {
return (
<Stack.Navigator >
<Stack.Screen
name='Berita'
component={Berita}
options={{
headerTitleAlign: 'center'
}}/>
<Stack.Screen
name="Detail"
component={Detail}
options={{
headerTitleAlign: 'center'
}}/>
</Stack.Navigator>
)
}
It appears that you are using Stack Navigators with different screen names, but you didn't send it. If possible, can you send that file, I would be able to help you a bit better. But from what I have I can try and explain how Stack navigation works. With this line of code:
navigation.navigate('Detail')
You specify that you want to navigate to the screen named "Detail", if you want to navigate to another screen then you can change it to the name of the screen. Let's say you want to navigate to your home screen, and its named "Home" in your Stack.Navigation component. Simply change your navigation to the following:
navigation.navigate('Home')
This is happening because the navigation prop is passed to the Berita component and you are destructuring the property in Item component not in Berita.
So the code should look like
...
const Berita = ({ navigation }) => {
// ...
return (
<ScrollView style={styles.container}>
{users.map(user => {
return (
<Item
key={user.id}
id={user.id}
user_id={user.user_id}
title={user.title}
navigation={navigation} // Pass navigation
/>
);
})}
</ScrollView>
);
};
Another way is - you can just use navigation in onPress (Berita) and pass down onPress to Item component
const Item = ({ id, user_id, title, onPress }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Id :{id}</Text>
<Text style={styles.text}>User Id :{user_id}</Text>
<Text style={styles.text}>Tittle :{title}</Text>
<View style={styles.container}>
<Button onPress={onPress} title="Detail" />
</View>
<View style={styles.line}></View>
</View>
);
};
const Berita = ({ navigation }) => {
const [users, setUsers] = useState([]);
useEffect(() => {
getData();
}, []);
const selectItem = item => {
console.log('Selected item: ', item);
};
const getData = () => {
axios.get('https://gorest.co.in/public/v1/posts').then(res => {
console.log('res: ', res);
setUsers(res.data.data);
});
};
return (
<ScrollView style={styles.container}>
{users.map(user => {
return (
<Item
key={user.id}
id={user.id}
user_id={user.user_id}
title={user.title}
onPress={() => navigation.navigate('Detail')}
/>
);
})}
</ScrollView>
);
};

Not sure how to remove this FlatList from displaying by default on screen

Currently, this - is how the SearchBar and FlatList is showing up on the screen. Upon clicking on the SearchBar and typing one of the list components, it shows up this- way. I want the FlatList to only appear when I click on the SearchBar. How do I implement this in the app? Something like a dropdown search bar...
import React from 'react';
import { View, Text, FlatList, ActivityIndicator, StyleSheet } from 'react-native';
import { SearchBar } from 'react-native-elements';
import { Button, Menu, Divider, Provider, TextInput } from 'react-native-paper';
const restaurantList = [
{
type: 'Italian',
name: 'DiMaggio'
},
{
type: 'Greek',
name: 'Athena'
}
];
export default class App extends React.Component {
static navigationOptions = {
title: 'Search for Restaurants'
};
constructor (props) {
super(props);
this.state = {
loading: false,
data: restaurantList,
error: null,
value: '',
};
this.arrayholder = [];
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%'
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text
});
const newData = restaurantList.filter(item => {
console.log(item.type)
const itemData = `${item.name.toUpperCase()} ${item.type.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.includes(textData);
});
this.setState({
data: newData
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type..."
value={this.state.value}
onChangeText={text => this.searchFilterFunction(text)}
/>
);
};
render () {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
} else {
return (
<Provider>
<View
style={{
paddingTop: 50,
flexDirection: 'row',
justifyContent: 'center',
}}>
<View style={styles.container}>
<FlatList
keyExtractor={(item, index) => `${index}`}
extraData={this.state}
data={this.state.data}
renderItem={({ item }) => (
<Text>{item.name} {item.type}</Text>
)}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
</View>
<View style={styles.container}>
<TextInput />
</View>
</Provider>
);
}
}
}
Try this way
this.state = {
searchClicked: false
};
<SearchBar
placeholder="Type..."
value={this.state.value}
onChangeText={text => this.searchFilterFunction(text)}
onFocus={() => this.setState({searchClicked: true})}
onBlur={() => this.setState({searchClicked: false})}
/>
<View>{this.renderHeader()}</View>
{this.state.searchClicked && <FlatList
keyExtractor={(item, index) => `${index}`}
extraData={this.state}
data={this.state.data}
renderItem={({ item }) => (
<Text>{item.name} {item.type}</Text>
)}
ItemSeparatorComponent={this.renderSeparator}
// ListHeaderComponent={this.renderHeader} <—- Remove from here —->
/>}
In case #Lakhani reply doesn't satisfy your question, I have another suggestion for you.
Your SearchBar should contain a TextInput. You can use onFocus and onBlur props to capture event you click on SearchBar or leave it.
...
<SearchBar
placeholder="Type..."
value={this.state.value}
onChangeText={text => this.searchFilterFunction(text)}
onFocus={()=>this.setState({showList: true})} // <---
onBlur={()=>this.setState({showList: false})} // <---
/>
...
render() {
return (
...
{
this.state.showList && <FlatList ... />
}
)
}

too many rerenders on using useState

I am calling the contactsfunction from the main return. I don't see any error until here:
const showContacts = React.useCallback(
(data: UsersQueryHookResult) => {
if (data) {
return (
<View style={styles.users}>
</View>
);
}
},
[userData],
);
const contacts = () => {
console.log('running');
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 34 },
},
});
console.log('DATA COMING', data);
//setUserData(data);
//console.log('contact name', data.users.nodes[0].userRelations[0].relatedUser.firstName);
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon
name="add"
onPress={() => navigation.navigate('AddContactTry')}
/>
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
<View style={{ width: moderateScale(350) }}>
<Text>Keine Kontacte</Text>
</View>
{contacts()}
</Container>
</SafeAreaView>
);
};
Now I want to do some conditional rendering on the basis of the results from contacts. However, as soon as I uncomment setUserData(data); I get an error that too many re-renders. I don't get what I am doing wrong in showUsersto get this error.
Edit:
I tried this but it gives an invalid hook call error:
export const Whitelist: React.FunctionComponent = (props) => {
const [userData, setUserData] = useState<UsersQueryHookResult>('');
useEffect(() => {
// Your function to fetch/get contacts
const data = contacts();
setUserData(data);
}, [])
const showContacts = React.useCallback(
(data: UsersQueryHookResult) => {
if (data) {
return (
<View style={styles.users}>
</View>
);
}
},
[userData],
);
const contacts = () => {
console.log('running');
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 34 },
},
});
console.log('DATA COMING', data);
//setUserData(data);
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon
name="add"
onPress={() => navigation.navigate('AddContactTry')}
/>
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
{/* {contacts()} */}
</Container>
</SafeAreaView>
);
};
Try to fetch contacts in componentDidMount method or useEffect (in your case with hooks) and store the result in a state with useState.
const [contacts, setContacts] = React.useState(null);
[...]
React.useEffect(() => {
// Your function to fetch/get contacts
const data = contacts();
setContacts(data);
}, [])
[...]
return (
<SafeAreaView style={{ flex: 1 }}>
[...]
{contacts && contacts.map(contact => (<View>{contact.name}</View>)}
[...]
</SafeAreaView>
);
hope it helps.

React Native pass data into modal

I'm still new in react native and programming, and i am trying to pass items from my flat list into a modal. What i'm about to pass is the icon, status, and description. How am i supposed to do that?
this is my flatlist
buildPanel(index, item) {
let panel = [];
let keys = DBkeys['Requests'].MyRequest;
let status = item[keys['status']];
panel.push(<View style={{ position: 'absolute', right: 0, bottom: 0, padding: normalize(5), alignItems: 'center' }} key={'status'}>
<TouchableOpacity onPress={this.handleShowModal}>
<Icon name={img.itemStatus[status].name} type={img.itemStatus[status].type} color={img.itemStatus[status].color} size={normalize(38)} />
</TouchableOpacity>
</View>);
return panel;
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal}/>
and this is my IconModal.js
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon name='open-book' type='entypo' color='#ffb732' size={normalize(70)} />
</View>
<View style={styles.text}>
<Text style={styles.status}>Status</Text>
<Text>Desc</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
IconModal.propTypes ={
visible: PropTypes.bool.isRequired,
close: PropTypes.func,
}
from the renderItem of your FlatList,
You must be clicking somewhere to open modal,
when you click store that whole single item in state variable,
like, if you're using TouchableOpacity then
<TouchableOpacity onPress={this.passDataToModal}/>
...
...
passDataToModal=(item)=>{
this.setState({modalData:item},()=>{
//you can open modal here
});
}
and in your modal component,
you can pass data with prop.
<IconModal modalData={this.state.modalData} visible={this.state.modalVisible} close={this.handleDismissModal}/>
and you can use these data in IconModal as this.props.modalData.
If there is more data then you can always add another prop.
Define the following Hooks in your function Component.
const [modalVisible, setModalVisible] = useState(false);
const [modalData, setModalData] = useState([]);
const [modalTitle, setModalTitle] = useState('');
Now Trigger the function which opens the Modal, while simultaneously passing data into it.
<TouchableHighlight underlayColor="skyblue" onPress={() => { openSettingsModal(title,settings) } }>
Open Modal
</TouchableHighlight>
Here is the function code -
const openSettingsModal = (title,settings) => {
setModalTitle(title);
setModalData(settings);
setModalVisible(!modalVisible);
}
And finally a snippet of the Modal Code.
<Modal animationType="none" transparent={true} visible={modalVisible} >
<View style={styles.centeredView}>
<Text> { modalTitle }</Text>
<Text> { modalData }</Text>
</View>
</Modal>
For example:
class Container extends Component {
constructor(props) {
super(props)
this.state = {
modalVisible: false,
activeItemName: '', //state property to hold item name
activeItemId: null, //state property to hold item id
}
}
openModalWithItem(item) {
this.setState({
modalVisible: true,
activeItemName: item.name,
activeItemId: item.id
})
}
render() {
let buttonList = this.props.item.map(item => {
return (
<TouchableOpacity onPress={() => { this.openModalWithItem(item) }}>
<Text>{item.name}</Text>
</TouchableOpacity>
)
});
return (
<View>
{/* Example Modal Component */}
<Modal isOpen={this.state.openDeleteModal}
itemId={this.state.activeItemId}
itemName={this.state.activeItemName} />
{buttonList}
</View>
)
}
}

Categories