React Native - Passing navigation into a child component - reactjs

I have a screen with the following:
function Intereset ({ navigation }) {
function ReturnMyFunction () {
if (!var.length) {
return ( <NoILikes /> )
} else {
return (
<FlatList
data={Ilike}
keyExtractor={item => item._id}
ItemSeparatorComponent={() => <Divider />}
renderItem={UserRow}
/>
)
}
}
return ( <ReturnILikeOrNoILike /> )
}
export default Interest
Here is my UserRow component below:
const UserRow = ({ item, navigation }) => (
<TouchableOpacity onPress={() => navigation.navigate("ProfileDetailScreenSingle", { userID: item.likeTo })}>
<View style={styles.row}>
<Image style={styles.avatar}
resizeMode={"cover"}
source={{ uri: item.likeToProfileImage }}
/>
<View style={styles.textContainer}>
<Text style={styles.name}>{item.likeToName}, <Text>{item.likeToAge}</Text></Text>
</View>
<Text style={styles.viewProfileText}>View Profile</Text>
<AntDesign name="right" size={14} color="black" />
</View>
</TouchableOpacity>
)
When I click on the UserRow to navigate I get the following issue.

I'm using useNavigation to redirect screen in stack:
import { useNavigation } from "#react-navigation/native";
const UserRow = ({ item }) => {
const navigation = useNavigation()
const onGoToProfileDetailScreenSingle = () => navigation.navigate("ProfileDetailScreenSingle", { userID: item.likeTo })
return (
<TouchableOpacity onPress={onGoToProfileDetailScreenSingle}>
...
</TouchableOpacity>
);
}
const renderItem = ({ item }) => <UserRow item={item} />
return (
<FlatList
data={Ilike}
renderItem={renderItem}
...
/>
)

Use onPress handler instead and perform navigation on the screen.
const UserRow = ({ item, onPress }) => (
<TouchableOpacity onPress={onPress}>
...
</TouchableOpacity>
);
const renderItem = ({ item }) => {
return (
<UserRow
item={item}
onPress={() => navigation.navigate(...)}
/>
);
};
<FlatList
data={Ilike}
renderItem={renderItem}
...
/>

Related

Mobx store do not update with observer

I have a simple react native app with two screens.
First screen is the list, where you see your selected groups, and you can remove them, by clicking on trash icon:
export const Settings: NavioScreen = observer(({ }) => {
...
return (
<View flex bg-bgColor padding-20>
<FlashList
contentInsetAdjustmentBehavior="always"
data={toJS(ui.savedGroups)}
renderItem={({item}) => <ListItem item={item} />}
estimatedItemSize={20}
/>
</View>
);
});
};
const ListItem = ({item}: any) => {
const { ui } = useStores();
return (
<View>
<Text textColor style={{ fontWeight: 'bold', fontSize: 15 }}>{item.name}</Text>
<TouchableOpacity onPress={() => ui.deleteGroup(item)}>
<Icon name={'trash'}/>
</TouchableOpacity>
</View>
);
};
The second screen is also the list, where you can add and remove the subjects from the list:
export const Playground: NavioScreen = observer(() => {
...
const groupsToShow =
ui.search && ui.search.length > 0
? ui.groups.filter((p) =>
p.name.toLowerCase().includes(ui.search.toLowerCase())
)
: ui.groups;
return (
<View >
<FlashList
data={toJS(groupsToShow)}
renderItem={({item}) => <ListItem item={item} />}
/>
</View>
);
});
const ListItem = ({item}: any) => {
const { ui } = useStores();
return (
<View>
<Text textColor style={{ fontWeight: 'bold', fontSize: 15 }}>{item.name}</Text>
<View>
<If
_={ui.isGroupSaved(item)}
_then={
<TouchableOpacity onPress={(e) => {ui.deleteGroup(item)}}>
<AntDesign name="heart" size={20} color={Colors.primary} />
</TouchableOpacity>
}
_else={
<TouchableOpacity onPress={(e) => {ui.addGroup(item)}}>
<AntDesign name="hearto" size={20} color={Colors.primary} />
</TouchableOpacity>
}
/>
</View>
</View>
);
};
And now when I remove the group from the first list, the heart icon do not update on the second list. But it should, because there is an if statement on second list, that checks if the group is saved. And if it is not, the heart should have the name="hearto"
I have tried to use the state instead mobx library but it does not also help.
Here is my store written with mobx:
export class UIStore implements IStore {
savedGroups = [];
constructor() {
makeAutoObservable(this);
makePersistable(this, {
name: UIStore.name,
properties: ['savedGroups'],
});
}
addGroup = (group: any) => {
if (true === this.isGroupSaved(group)) {
return;
}
this.savedGroups.push(group);
}
isGroupSaved = (group: any) => {
return -1 !== this.savedGroups.findIndex(g => g.id === group.id);
}
deleteGroup = (groupToDelete: any) => {
this.savedGroups = this.savedGroups.filter((group) => group.id !== groupToDelete.id);
}
}

React Native Arrow Down or Arrow Right When Toggle Collapse

I want only my selected FAQ toggle arrow right will be change to arrow down. But for now my code will change All my FAQ arrow right to down when toggle call.
This is my code :
const renderItem = ({ item }, open, show) => {
return (
<View style={styles.container}>
<Collapse onToggle={() => show()}>
<CollapseHeader>
<View style={styles.section}>
<Text style={styles.question}>{item.question}</Text>
{open ? <AntDesign name="down" size={15} color="gray" /> : <AntDesign name="right" size={15} color="gray" />}
</View>
</CollapseHeader>
<CollapseBody>
<Text style={styles.answer}>{item.answer}</Text>
</CollapseBody>
</Collapse>
</View>
);
};
const FAQComponent = () => {
console.log('FAQComponent');
const [open, setOpen] = useState(false);
const show = () => {
setOpen(!open);
}
return (
<FlatList
data={Object.values(FAQData)}
renderItem={(item) => renderItem(item, open, show)}
keyExtractor={item => item.id}
initialNumToRender={10}
/>
);
};
This is the default layout arrow right:
And This is when one of FAQ collapse :
How do I make only selected FAQ when collapse the arrow change to down and the other remain same ?
Thank You.
const renderItem = (item, seletecdItem, show, index) => {
// Instead of Passing a Boolean pass a SelectedItem which holds the index of current selected index and verify it with that
return (
<View style={styles.container}>
<Collapse onToggle={() => show(index)}>
<CollapseHeader>
<View style={styles.section}>
<Text style={styles.question}>{item.question}</Text>
{seletecdItem === index ? (
<AntDesign name="down" size={15} color="gray" />
) : (
<AntDesign name="right" size={15} color="gray" />
)}
</View>
</CollapseHeader>
<CollapseBody>
<Text style={styles.answer}>{item.answer}</Text>
</CollapseBody>
</Collapse>
</View>
);
};
const FAQComponent = () => {
console.log('FAQComponent');
const [selectedItem, setSelectedItem] = useState(-1); // Change the useState to use number
const show = (index) => { // Pass Index through the callback and update the state
setSelectedItem(index);
};
return (
<FlatList
data={Object.values(FAQData)}
renderItem={{ item,index } => renderItem(item, selectedItem, show, index)} // Pass the Index along with renderItem
keyExtractor={item => item.id}
initialNumToRender={10}
/>
);
};

React Native: Render different component based on button press

I am new to React/React Native and am currently building a screen that consists of a section consisting of 3 buttons and a space below to render different components depending on which tab button is pressed. The tabs are add, remove, and history.
Basically the intent is to recreate something like the tabs component from React Bootstrap, but for React Native. I have seen the React Navigation tabs component, but I do not want the tabs to navigate to a different screen, just render a different component depending on which is clicked and have it fade into the section below the buttons.
Below is a rough idea of how I am thinking of approaching the problem.
const ChoiceContainer = props => {
const {children} = props;
render(<View>{children}</View>);
};
const AddEntry = () => (
<ChoiceContainer>
<Card>
<View style={{paddingLeft: 5}}>
<Text style={styles.cardTitle}>Component 1</Text>
</View>
</Card>
</ChoiceContainer>
);
const RemoveEntry = () => (
<ChoiceContainer>
<Card>
<View style={{paddingLeft: 5}}>
<Text style={styles.cardTitle}>Component 2</Text>
</View>
</Card>
</ChoiceContainer>
);
const EntryHistory = () => (
<ChoiceContainer>
<Card>
<View style={{paddingLeft: 5}}>
<Text style={styles.cardTitle}>Component 3</Text>
</View>
</Card>
</ChoiceContainer>
);
export default class EntryTabs extends Component {
showAdd = () => {
this.setState({sceneType: 'add'});
};
showRemove = () => {
this.setState({sceneType: 'receive'});
};
showHistory = () => {
this.setState({sceneType: 'history'});
};
renderScene = type => {
if (type === 'add') {
return <AddEntry />;
}
if (type === 'remove') {
return <RemoveEntrty />;
}
if (type === 'history') {
return <EntryHistory />;
}
};
render() {
const {sceneType} = this.state;
render(
<SafeAreaView>
<View style={{flex: 1}}>
return (
<EntryCard
/>
);
})}
<View style={{flex:1}}>
<View>
<TouchableOpacity onPress={this.showAdd}>
<Text> Add </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showRemove}>
<Text> Remove </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showHistory}>
<Text> History </Text>
</TouchableOpacity>
</View>
<View>{this.renderScene(sceneType)}</View>
</View>,
</View>
</SafeAreaView>
);
}
}
Any help/guidance would huge hugely appreciated. Thanks!
You have not defined the state, And you have to use map to show the entries.
The working version of you code should be as below.
const value1 = 1,
value2 = 2,
value3 = 3;
const entries = [
{
key1: value1,
key2: value2,
key3: value3,
},
{
key1: value1,
key2: value2,
key3: value3,
},
{
key1: value1,
key2: value2,
key3: value3,
},
{
key1: value1,
key2: value2,
key3: value3,
},
{
key1: value1,
key2: value2,
key3: value3,
},
];
const ChoiceContainer = (props) => {
const { children } = props;
return <View>{children}</View>;
};
const AddEntry = () => (
<ChoiceContainer>
<Card>
<View style={{ paddingLeft: 5 }}>
<Text style={styles.cardTitle}>Add Entry</Text>
</View>
</Card>
<TextInput value={entries.key1} />
</ChoiceContainer>
);
const RemoveEntry = () => (
<ChoiceContainer>
<Card>
<View style={{ paddingLeft: 5 }}>
<Text style={styles.cardTitle}>Remove Entry</Text>
</View>
</Card>
<TextInput value={entries.key2} />
</ChoiceContainer>
);
const EntryHistory = () => (
<ChoiceContainer>
<Card>
<View style={{ paddingLeft: 5 }}>
<Text style={styles.cardTitle}>Entry History</Text>
</View>
</Card>
<TextInput value={entries.key3} />
</ChoiceContainer>
);
class EntryTabs extends React.Component {
state = {
sceneType: 'add',
};
showAdd = () => {
this.setState({ sceneType: 'add' });
};
showRemove = () => {
this.setState({ sceneType: 'receive' });
};
showHistory = () => {
this.setState({ sceneType: 'history' });
};
renderScene = (type) => {
if (type === 'add') {
return <AddEntry />;
}
if (type === 'remove') {
return <RemoveEntry />;
}
if (type === 'history') {
return <EntryHistory />;
}
};
render() {
const { sceneType } = this.state;
return (
<SafeAreaView>
<View style={{ flex: 1 }}>
{entries.map((item) => {
return (
<EntryHistory
baseAsset={item.key1}
logo={item.key2}
screen={item.key3}
/>
);
})}
<View style={{ flex: 1 }}>
<View>
<TouchableOpacity onPress={this.showAdd}>
<Text> Add </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showRemove}>
<Text> Remove </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showHistory}>
<Text> History </Text>
</TouchableOpacity>
</View>
<View>{this.renderScene(sceneType)}</View>
</View>
,
</View>
</SafeAreaView>
);
}
}
function CustomText(props) {
const [showMore, setShowMore] = React.useState(false);
const [lines, setLines] = React.useState(props.numberOfLines);
const onTextLayout = (e) => {
setShowMore(
e.nativeEvent.lines.length > props.numberOfLines && lines !== 0
);
};
return (
<View>
<Text numberOfLines={lines} onTextLayout={onTextLayout}>
{props.children}
</Text>
&&{' '}
{showMore && (
<Button
title="Show More"
onPress={() => {
setLines(0);
setShowMore(false);
}}
/>
)}
{!showMore && (
<Button
title="Hide More"
onPress={() => {
setLines(props.numberOfLines);
}}
/>
)}
</View>
);
}

Invalid use of hooks when calling component with onPress

I'm trying to work with modals when I click on a button from the header.
Say I have this component List, and List is using custom navigation options:
import { CustomModal } from './components/Modal';
const List = (props) => {
const [enteredUrl, setEnteredUrl] = useState('');
const urlInputHandler = (enteredUrl) => {
setEnteredUrl(enteredUrl);
};
const addUrlHander = () => {
console.log(enteredUrl);
}
return (
<View></View>
);
};
List.navigationOptions = (navData) => {
return {
headerTitle: 'Workouts',
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Add'
iconName='md-add'
onPress={() => {
CustomModal(); //here is the modal
}}
/>
</HeaderButtons>
),
headerBackTitle: null
};
};
My Modal component has this:
export const CustomModal = (props) => {
const [modalVisible, setModalVisible] = useState(false);
console.log(props);
return (
<Modal
animationType='slide'
transparent={false}
visible={modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}
>
<View style={{ marginTop: 22 }}>
<View>
<Text>Hello World!</Text>
<TouchableHighlight
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
);
}
But it is giving me the invalid hook error. Why is it that my onPress in my navigationOptions giving me this? Am I doing this wrong?
onPress is a callback, you can't put components in it. Probably what you want is something like this:
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<CustomModal/>
</HeaderButtons>
and the modal looks like
export const CustomModal = (props) => {
const [modalVisible, setModalVisible] = useState(false);
console.log(props);
return modalVisible?(
<Modal
animationType='slide'
transparent={false}
visible={modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}
>
<View style={{ marginTop: 22 }}>
<View>
<Text>Hello World!</Text>
<TouchableHighlight
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
):(
<Item
title='Add'
iconName='md-add'
onPress={() => setModalVisible(!modalVisible)}
/>
);
}

useState-hook is not updating my state in an onPress() function

I am building a tab-view and I cannot figure out why the useState-hook is not updating my state. I am sure it is something easy, but I have been stumped here a while now...
I can see that the onPress function is fired and that item.label is the correct one if I log that out. However, setState does not change, even if I hardcode the parameter.
const TabContainer = ({ tabs }) => {
const [selected, setSelected] = useState('');
function renderItem(item, index) {
return item.label === selected ? (
<View style={styles.selectedTab}>
<CustomText style={styles.tabText}>{item.label}</CustomText>
</View>
) : (
<TouchableWithoutFeedback
key={index}
onPress={() => {
console.log(selected);
setSelected(item.label);
}}
>
<View style={styles.tab}>
<CustomText style={styles.tabText}>{item.label}</CustomText>
</View>
</TouchableWithoutFeedback>
);
}
return (
<View style={styles.tabContainer}>
<FlatList
keyExtractor={(item, index) => index.toString()}
data={tabs}
renderItem={({ item, index }) => {
return renderItem(item, index);
}}
style={styles.listContainer}
/>
</View>
);
};
export default TabContainer;
Try this :
<
TouchableWithoutFeedback
key={index}
onPress={() => onPressItem(item)}
>
const onPressItem =(item)=>{
setSelected(item.label)}
Sometimes, using useRef instead of useState when you want to set a value at a different stage in the lifecycle than the constructor.
Please take a look at this and try to implement this in your code:
[https://codesandbox.io/s/v6948pww5y?from-embed]
Okey I kinda solved this. I realized that I had to lift my state up for my tabs in order to render the other stuff properly. So then I just implemented in the same way in the parent component, and then it worked...
Important rows from parent:
const [selectedTab, setSelectedTab] = useState('');
function handleTabPress(tab) {
setSelectedTab(tab);
}
...
<TabContainer
tabs={[
{ label: 'Label 1' },
{ label: 'Label 2' },
{ label: 'Label 3' },
]}
selectedTab={selectedTab}
handleTabPress={handleTabPress}
/>
New child
const TabContainer = ({ tabs, selectedTab, handleTabPress }) => {
function renderItem(item, index) {
return item.label === selectedTab ? (
<View style={styles.selectedTab}>
<CustomText style={styles.selectedTabText}>{item.label}</CustomText>
</View>
) : (
<TouchableWithoutFeedback
key={index}
onPress={() => {
handleTabPress(item.label);
}}
>
<View style={styles.tab}>
<CustomText style={styles.tabText}>{item.label}</CustomText>
</View>
</TouchableWithoutFeedback>
);
}
return (
<View style={styles.tabContainer}>
<FlatList
keyExtractor={(item, index) => index.toString()}
data={tabs}
renderItem={({ item, index }) => {
return renderItem(item, index);
}}
style={styles.listContainer}
/>
</View>
);
};

Resources