I have a list of items in my app. When the user clicks on a list item I want to show more details in a . I could put the details-component into the list item component and hide it by default.
Is there a better way?
Assuming you will only be displaying a single Modal at a time. You can create the Modal component outside of the list and set it to visible on click of the list item. So you code will be something like this.
class YourComponent extends Component {
state = {
showModal: false,
};
render() {
const items = [1, 2, 3];
return (
<View>
<View>
{
items.map((item, i) => {
<Button key={i} onPress={() => this.setState({ showModal: true, item }) title="Open Modal" />
})
}
</View>
<ReactNative.Modal visible={this.state.showModal}>
<Text>{ this.state.item }</Text>
</ReactNative.Modal>
</View>
);
}
}
Related
im using navigation to pass between screens.
now,im trying to figure out how can i get a value from second screen to the first screen ?
the user needs to pick a color value from the second screen and return selcted color to the first screen.
this is the code im using .
enter code here
<CustomButton
style={styles.buttonPicker}
darkMode={this.props.darkMode}
title={'pick a color'}
onPress={() => {
this.props.navigation.navigate('ColorPickerScreen', {
onSubmit: (namecolor) => {
console.log('55555555555555', { getNameColor })
},
})
}}
></CustomButton>
enter code here
onSelect = (color) => this.props.navigation.navigate('CreatenewtTipul')
render() {
return (
<Image
style={styles.img}
source={require('../components/icons/color-wheel.png')}
/>
<ColorPicker
colors={this.state.colors}
selectedColor={this.state.selectedColor}
onSelect={this.onSelect}
/>
<Text>Selected Color = {this.state.selectedColor}</Text>
</View>
)
}
}
tnx for any help
arik :)
To pass value from screen A to screen B:
navigation.navigate('ScreenB', {
itemId: 86,
otherParam: 'anything you want here',
});
To access that value in Screen A:
const { itemId, otherParam } = route.params;
Where were route here is part of the screen's props, check the guide here for more info
im not trying to pass a value to the second screen.
im trying to get a value from the second screen to the first screen.
You can pass a function as a callback from the first screen to the second screen in params on call that on your second screen.
function Screen1(props) {
const onSelect = (selectedColor) => {
console.log('selectedColor', selectedColor)
}
const navigateToSecondScreen = () => {
props.navigation.navigate('Screen2', {
onColorSelect: onSelect
})
}
return(
<View>
<TouchableOpacity onPress={navigateToSecondScreen}>
<Text>Go to second screen</Text>
</TouchableOpacity>
</View>
)
}
//Second Screen
function Screen2(props) {
const {onColorSelect} = props.route.params;
return(
<View>
<TouchableOpacity onPress={() => {onColorSelect('color value')}}>
<Text>your other code here</Text>
</TouchableOpacity>
</View>
)
}
The idea is just to call the function which you have passed as a param from Screen1
Senario
I have a dialog, it look something like this, it have hook for showing this dialog, called showDialog , and dialog have a button with Onpress method
export default function DialogTesting(show: boolean) {
const [showDialog, doShow] = useState(show)
return (
<View>
{/* <Button
title="click"
onPress={() => {
setShow(true)
}}
>
<Text>Show dialog</Text>
</Button> */}
<Dialog
visible={showDialog}
title="Record New Progress"
style={DIALOG}
onClose={() => {
doShow(false)
}}
>
And a main sceen , it also have hook to show dialog, called show,
export const MedicalRecord = memo(function MedicalRecord() {
// const onPressViewAll = useCallback(() => {}, [])
const [show, setShow] = useState(false)
function hanndleDialog() {
setShow(!show)
}
return (
<SummaryViewContainer
count={5}
title={"dashboardScreen.medicalRecords.title"}
onPress={() => {
hanndleDialog()
}}
>
<View>
{show && (
<ProgressDialog
show={show}
callback={() => {
hanndleDialog()
}}
/>
)}
<RecordItem />
<RecordItem />
<RecordItem />
</View>
</SummaryViewContainer>
)
})
Problem
When i click the button in main screen, change hook show to true to show dialog, and use this hook state in dialog to set show in dialog to true to show that dialog, and in dialog, when i click button to close dialog, it disappear, but problem is, the state of show in main screen remain true, so i have to press twice to show dialog again
Question
How can i change hook status in main screen, or how can i press close button in dialog, the show hook in main screen return false, i tried to change state of mainscreen in dialog but it won't work
Here is a short video of problem
https://streamable.com/9mm26t
You should maintain just one copy of your state in the parent component itself. Then pass "show" and "setShow" as props to the child component.
Parent Component:
export const MedicalRecord = memo(function MedicalRecord() {
// const onPressViewAll = useCallback(() => {}, [])
const [show, setShow] = useState(false)
function hanndleDialog() {
setShow(!show)
}
return (
<SummaryViewContainer
count={5}
title={"dashboardScreen.medicalRecords.title"}
onPress={() => {
hanndleDialog()
}}
>
<View>
{show && (
<ProgressDialog
show={show}
setShow = {setShow}
/>
)}
<RecordItem />
<RecordItem />
<RecordItem />
</View>
</SummaryViewContainer>
)
})
Dialog Component:
export default function DialogTesting({show, setShow}) {
return (
<View>
{/* <Button
title="click"
onPress={() => {
setShow(true)
}}>
<Text>Show dialog</Text>
</Button> */}
<Dialog
visible={show}
title="Record New Progress"
style={DIALOG}
onClose={() => {
setShow(false)
}}
>
</View>
)
}
It looks like you have 2 versions of local state at each component. You need to keep 1 version of "show" at the top most parent where you care to control this variable and then pass it down as a prop to your child components. Then on your child components you need to expose a callback that the parent will pass down and the child will call to trigger what occurs when the button is clicked in the child component.
I have two screens, one list (Flatlist) and one filter screen where I want to be able to set some filters for the list. the list screen has the states "data" and "usedFilters". When I am switching to the filters screen, the states are set as navigation parameters for react navigation and then passed via navigation.navigate, together with the onChange function, as props to the filter screen. There they are read, and the filters screen class' state is set (usually with passed filters from the list screen, if no valid filters has been passed, some are initialized).
After that the filters can be changed. If that happens, the state of the filter screen gets updated.
If then the apply button is clicked the filter screens' state is passed to the onChange function and via that back to the list screen, the onChange function updates the state "usedFilters" state of the list screen. If the cancel button is pressed null is passed to the onChange function and there is no setState call.
Setting new states for the list screen works perfectly fine. the problem is, that when i press the cancel button (or the back button automatically rendered by react navigation) the changes are kept nevertheless. That only happens if the state has been changed before. So if there has never been applied a change and hence the "usedFitlers" state of the list screen is null, this behavior does not occur. Only if I already made some changes and hence the "usedFitlers" state of the list screen has a valid value which is passed to the filters screen the cancel or go back buttons won't work as expected.
I am using expo-cli 3 and tried on my android smartphone as well as the iOS simulator. Same behavior. I looked into it with chrome dev tools as well but i simply couldn't figure out where the "usedFitlers" state was updated.
I am using react native 0.60 and react navigation 3.11.0
My best guess is that for some reason the two states share the same memory or one is pointer to the other or sth like that. (Had problems like that with python some time ago, not knowing the it uses pointers when assigning variables).
Anyone got an idea?
List Screen:
export default class ListScreen extends React.Component {
state = { data: [], usedFilters: null };
static navigationOptions = ({ navigation }) => {
let data = navigation.getParam('data')
let changefilter = navigation.getParam('changeFilter')
let currfilter = navigation.getParam('currFilter')
return {
headerTitle:
<Text style={Styles.headerTitle}>{strings('List')}</Text>,
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('FilterScreen', {
dataset: data, onChange: changefilter, activeFilters:
currfilter })} >
<View paddingRight={16}>
<Icon name="settings" size={24} color=
{Colors.headerTintColor} />
</View>
</TouchableOpacity>
),
};
};
_onChangeFilter = (newFilter) => {
if (newFilter) {
this.setState({ usedFilters: newFilter })
this.props.navigation.setParams({ currFilter: newFilter });
} // added for debugging reasons
else {
this.forceUpdate();
let a = this.state.usedFilters;
}
}
_fetchData() {
this.setState({ data: fakedata.results },
() => this.props.navigation.setParams({ data: fakedata.results,
changeFilter: this._onChangeFilter }));
}
componentDidMount() {
this._fetchData();
}
render() {
return (
<ScrollView>
<FlatList/>
// Just data rendering, no problems here
</ScrollView>
);
}
}
Filter Screen:
export default class FilterScreen extends React.Component {
static navigationOptions = () => {
return {
headerTitle: <Text style={Styles.headerTitle}> {strings('filter')}
</Text>
};
};
state = { currentFilters: null }
_onChange = (filter, idx) => {
let tmp = this.state.currentFilters;
tmp[idx] = filter;
this.setState({ currentFilters: tmp })
}
_initFilterElems() {
const filters = this.props.navigation.getParam('activeFilters');
const dataset = this.props.navigation.getParam('dataset');
let filterA = [];
let filterB = [];
let filterC = [];
if (filters) {
// so some checks
} else {
// init filters
}
const filterElements = [filterA, filterB, filterC];
this.setState({ currentFilters: filterElements })
}
componentDidMount() {
this._initFilterElems()
}
render() {
const onChange = this.props.navigation.getParam('onChange');
return (
<ScrollView style={Styles.screenView}>
<FlatList
data={this.state.currentFilters} // Listeneinträge
keyExtractor={(item, index) => 'key' + index}
renderItem={({ item, index }) => (
<FilterCategory filter={item} name={filterNames[index]}
idx={index} onChange={this._onChange} />
)}
ItemSeparatorComponent={() => <View style=
{Styles.listSeperator} />}
/>
<View style={Layout.twoHorizontalButtons}>
<TouchableOpacity onPress={() => {
onChange(this.state.currentFilters);
this.setState({ currentFilters: null });
this.props.navigation.goBack();
}}>
<View style={Styles.smallButton}>
<Text style={Styles.buttonText}>{strings('apply')} </Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => {
onChange(null);
this.setState({ currentFilters: null });
this.props.navigation.goBack();
}}>
<View style={Styles.smallButton}>
<Text style={Styles.buttonText}>{strings('cancel')}
</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView >
);
}
}
So when I press the cancel button, null is returned to the _onChangeFilter function of the list screen. This part works, and according to console.log and the debugger, the setState is not called. But if i set a breakpoint within the else part, i can see that this.state.usedFilters has changed.
Ok after a while i figured it out. The problem was that the whole filters list was always just referenced since react native (js) seems to always use references, even when changing sub-parts of the lists.
fixed that by using lodash cloneDeep.
I have a FlatList and I am trying to but a button below the items and when you click on the button it should display the name of the item in an alert.
class TopMovies extends React.Component {
constructor(props) {
super(props);
this.state = {
apiTopLoaded: false,
topPopularMovies: [],
}
this.conditionalTopPopular = this.conditionalTopPopular.bind(this);
this.mybuttonclick = this.mybuttonclick.bind(this);
}
componentDidMount() {
fetch('someurls')
.then((response)=>{
response.json()
.then((popularMovies) => {
this.setState({
apiTopLoaded: true,
topPopularMovies: popularMovies.results,
})
})
})
mybuttonclick() {
Alert.alert(item.original_title)
}
conditionalTopPopular() {
if(this.state.apiTopLoaded===true) {
return(
<FlatList
horizontal={true}
data={this.state.topPopularMovies}
renderItem={({ item }) => (
<View>
<Text>{item.original_title}</Text>
<Button onPress={this.mybuttonclick} title="hello"/>
</View>
)}
keyExtractor={item => item.id}
/>
</View>
)
}
}
I can see all the movie names in the list and I see buttons below the movie names, but when I click on it, it says "cant find variable item". The function mybuttonclick should alert the item.original_title prop because it displays correctly in the flatlist. Please help.
Change your function like this:
mybuttonclick(movieTitle) {
Alert.alert(movieTitle)
}
And pass in the the movie title like this:
<Button onPress={this.mybuttonclick(item.original_title)} title="hello"/>
You should use the fat arrow function in the onPress like this.
<Button onPress={() => this.mybuttonclick(item.original_title)} title="hello"/>
I have a horizontal scrollview inside which I'm adding childviews which has button inside it . All i want to do is to highlight the selected child and dim the others(essentially update the style). I am able to update the selected child's style using state , but how do i update rest of the children of scrollview ? I have tried using refs, multiple states but none worked.
Can anyone please provide with some pointers as in how to go about it ?
Thanks ..!!
You should make the component that owns the child views be responsible for specifying whether they are highlighted or dimmed. That way all of the logic for choosing which child should be highlighted is in one place in your code, and you have to only re-render the owner component when the user taps one of the children.
To detect touch events on each child, wrap them in a TouchableWithoutFeedback component.
class OwnerComponent extends React.Component {
state = {
selectedChildIndex: null,
};
render() {
return (
<ScrollView>
<TouchableWithoutFeedback
onPressIn={() => {
this.setState({ selectedChildIndex: 0);
}}
onPressOut={() => {
this.setState({ selectedChildIndex: null);
}}
style={styles.touchableContainer}>
<View style={this.getChildStyle(0)} />
</TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPressIn={() => {
this.setState({ selectedChildIndex: 1);
}}
onPressOut={() => {
this.setState({ selectedChildIndex: null);
}}
style={styles.touchableContainer}>
<View style={this.getChildStyle(1)} />
</TouchableWithoutFeedback>
</ScrollView>
);
}
getChildStyle(childIndex) {
// No child is selected
if (this.state.selectedChildIndex === null) {
return null;
}
return (this.state.selectedChildIndex === childIndex) ?
styles.highlightedChild :
styles.dimmedChild;
}
}
let styles = StyleSheet.create({
touchableContainer: {
flexGrow: 1,
},
highlightedChild: {
backgroundColor: 0xf0f0f0;
},
dimmedChild: {
opacity: 0.7,
},
});