im building a music-player and i want to implement this:
at the bottom, theres a bottomsheet, so if I go to albums, artist, it will still there, how can i do it? for now i have but only in one screen, in this case Tracks:
render ()
const props = {
playing: this.state.playing,
update_playing: this.update_playing.bind(this),
update_next_song: this.update_next_song.bind(this),
repeat_song: this.repeat_song.bind(this),
repeat_queue: this.repeat_queue.bind(this),
show_hide_icon: this.show_hide_icon.bind(this),
current_track_id: this.state.current_track_id,
current_track: (this.state.current_track === "") ? this.props.info_track[0]?.title : this.state.current_track,
cover: (this.state.current_track === "") ? this.props.info_track[0]?.cover : this.state.cover,
artist: (this.state.current_track === "") ? this.props.info_track[0]?.artist : this.state.artist,
show_icon: this.state.show_icon,
tracks: this.props?.tracks,
first_id_song: this.props?.first_id_song,
last_id_song: this.props?.last_id_song,
}
return (
<>
<View style = {{flex: 1, backgroundColor: "black"}}>
<Text style = {{fontSize: 30, color: "red"}}>{saludo}</Text>
<FlatList
data = {this.props.tracks}
keyExtractor = {(item) => item.id.toString()}
renderItem = {({item}) => (
<TouchableOpacity onPress = {() => {this.play_selected_music(item) ; this.get_track_cover_artist(item)}}>
<View style = {this.styles.tracks_container}>
<View style = {this.styles.tracks_info_container}>
{
(item?.cover) ? <Image source = {{uri:"file:///"+item.cover}} style={{ width: 100, height: 100, marginLeft: 20}}></Image>
: <Image source = {require("../assets/default_image.jpg")} style={{ width: 100, height: 100, marginLeft: 20}}></Image>
}
<View style = {this.styles.tracks}>
<View>
<Text style = {{color: "white", fontSize: 20, marginLeft: 10}} numberOfLines = {1}>
{
(item.title.length > 20) ?
item.title.substring(0,18).padEnd(20,".")
: item.title
}
</Text>
</View>
<View style = {this.styles.artist_duration}>
<Text style = {{color: "white", fontSize: 10, marginLeft: 10}}>
Artist: {(item.artist.length > 15) ?
item.artist.substring(0,14).padEnd(16,".")
: item.artist}
</Text>
<Text style = {{color: "white",fontSize: 10, marginLeft: 10}}>
Duration: {this.get_time(item.duration)}
</Text>
</View>
</View>
</View>
</View>
</TouchableOpacity>
)}
>
</FlatList>
<BottomSheet
ref = {ref => (this.sheetref = ref)}
initialSnap = {1}
snapPoints = {[Dimensions.get("window").height - StatusBar.currentHeight, 95]}
renderHeader = {() => <Player_Header {...props}></Player_Header>}
renderContent = {() => <Track_Zone {...props}></Track_Zone>}
enabledContentGestureInteraction = {false}
onOpenEnd = {this.hide_icon}
onCloseEnd = {this.show_icon}>
</BottomSheet>
</View>
</>)
it receives props from the component to get update everytime the track changes, thought about putting it outside the navigator, but then, how can get all the necessary props, functions, etc to update it?
this is my navigator:
<Tab.Navigator>
<Tab.Screen name = "Tracks" children = {({navigation}) => <Tracks navigation = {navigation}></Tracks>}></Tab.Screen>
<Tab.Screen name = "Chao" children = {({navigation}) => <Chao navigation = {navigation}></Chao>}></Tab.Screen>
</Tab.Navigator>
You can define a HOC to render the BottomSheet withTracKBottomSheet()
Now you can wrap every screen in which there should be a BottomSheet with withTrackBottomSheet().
Something like this
const withTrackBottomSheet = Component => {
// Do all the business logic
return (
<>
<Component />
<BottomSheet />
</>
);
};
In your case, the same state will be shared among multiple screens/components. Thus I will advise you to use some state management library like redux to make your work a little easier and less complex.
Related
I'm trying to delete selected image from an array of images and yet only the first image on the list gets deleted whenever i tap any other image to delete. This is surprising cause i implemented the same method when generating a list in the same screen and it deletes the correct list item each time i tap on it to delete.
Here is all the code in the file excluding cascading styles. Its a lot but i Hope this helps.
const AddPost = () => {
const navigation = useNavigation();
// Thumbnail State
const [thumbnail, setThumbnail] = useState(null);
// Media State
const [media, setMedia] = useState([]);
// Details State
const [title, setTitle] = useState('');
const [category, setCategory] = useState('');
const [caption, setCaption] = useState('');
const [price, setPrice] = useState('');
const [details, setDetails] = useState(false);
// Store State
const [itemName, setItemName] = useState('');
const [itemPrice, setItemPrice] = useState('');
const [photoArray, setPhotoArray] = useState([]);
const [storeItems, setStoreItems] = useState([]);
const width = useWindowDimensions().width;
const numColumns = Math.ceil(width / 120);
const pickThumbnail = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.5,
});
if (!result.cancelled) {
setThumbnail(result.uri);
}
};
const pickMedia = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.2,
});
if (!result.cancelled) {
setMedia([...media, result.uri]);
}
};
const deleteMedia = (index) => {
let removeMedia = [...media];
removeMedia.splice(index, 1);
setMedia(removeMedia)
console.log(index)
};
const addDetailItem = () => {
if (title == '' || category == '' || caption == '' || price == '') {
Alert.alert('Error', 'Please fill in the item detail.')
} else {
Keyboard.dismiss();
setDetails(true);
detailsRef?.current?.close();
};
};
const detailsRef = useRef(null);
const toggleModal = () => {
detailsRef?.current?.present();
}
const toggleModalClose = () => {
detailsRef?.current?.close();
}
const addItem = {
id: Math.floor(Math.random() * 11),
itemName: itemName,
itemPrice: itemPrice,
photo: photoArray
};
const storeRef = useRef(null);
const toggleStoreModal = () => {
storeRef?.current?.present();
}
const toggleStoreModalClose = () => {
storeRef?.current?.close();
}
const addStoreItem = () => {
if (itemName == '' || itemPrice == '') {
Alert.alert('Error', 'Please fill in the item detail.')
} else {
Keyboard.dismiss();
setStoreItems([...storeItems, addItem]);
setItemName(null);
setItemPrice(null);
setPhotoArray([]);
};
};
const deleteItem = (index) => {
let removeItem = [...storeItems];
removeItem.splice(index, 1);
setStoreItems(removeItem)
};
const pickPhoto = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.2,
});
if (!result.cancelled) {
setPhotoArray([...photoArray, result.uri]);
}
};
const deletePhoto = (index) => {
let removeItem = [...photoArray];
removeItem.splice(index, 1);
setPhotoArray(removeItem)
};
const store = () => {
setStoreItems([...storeItems]);
storeRef?.current?.close();
Keyboard.dismiss();
};
return (
<View style={styles.container}>
{/* Header */}
<Header
title={'Add Post'}
leftComponent={
<View style={styles.headerLeft}>
<Image source={images.logo} style={{ height: 28, width: 28, tintColor: COLORS.primary2}} />
</View>
}
rightComponent={
<TouchableOpacity
style={{
width: 50,
height: 50,
alignItems: 'center',
justifyContent: 'center',
}}
onPress= {() => console.log('Post Sumbitted') }
>
<Image
source={icons.post2}
style={{
width: 35,
height: 35,
tintColor: COLORS.primary2,
}}
/>
</TouchableOpacity>
}
/>
<View style={styles.subContainer}>
<ScrollView>
<Text style={styles.textDetail}>Add a photo or video post with thumbnail, category, caption, price, and store details (Optional).</Text>
{/* Add Thumbnail */}
<AddPostItem label={'Thumbnail'} onPress={pickThumbnail}/>
{thumbnail &&
<View style={styles.thumbnail}>
<Image source={{ uri: thumbnail }} style={styles.thumbnailPhoto} />
</View>
}
{/* Add Media */}
<AddPostItem label={'Media (Video/Photo)'} onPress={pickMedia} />
<View style={{flexDirection: 'row'}}>
{Array.from(Array(numColumns)).map((col, colIndex) => (
<View style={{flex: 1}} key={colIndex}>
{media
.filter((item, index) => index % numColumns === colIndex)
.map((item, index) => (
<View key={index.toString()} style={styles.mediaContainer} >
<Image source={{uri: item}} style={styles.addedMedia}/>
<TouchableOpacity style={styles.imageCancel} key={index} onPress={() => deleteMedia(index)}>
<Image source={icons.cross} style={styles.imageCancelIcon}/>
</TouchableOpacity>
</View>
))}
</View>
))
}
</View>
{/* Add Details */}
<AddPostItem label={'Details (Title, Caption, etc)'} onPress={toggleModal}/>
{details &&
<View style={{marginHorizontal: 20}}>
<View style={{flexDirection: 'row', }}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Title: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>{title}</Text>
</View>
<View style={{flexDirection: 'row', marginTop: 5,}}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Category: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>{category}</Text>
</View>
<View style={{flexDirection: 'row', marginTop: 5,}}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Price: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>N{price}</Text>
</View>
<View style={{marginTop: 5,}}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Caption: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>{caption}</Text>
</View>
</View>
}
{/* Add Store */}
<AddPostItem label={'Store Items (Optional)'} onPress={toggleStoreModal}/>
{storeItems.map((item, index) => {
return (
<View key={index.toString()} style={styles.itemContainer}>
<Text style={styles.itemName}>{item?.itemName}</Text>
<Text style={styles.itemPrice}>N{item?.itemPrice}</Text>
</View>
)
})
}
<View>
</View>
</ScrollView>
</View>
<BottomSheetModal
enablePanDownToClose={true}
ref={detailsRef}
snapPoints={['100%']}
index={0}
backgroundComponent={
({style}) =>
<View style={[style, {
backgroundColor: COLORS.primary,
borderRadius: 30,
}]}/>}
>
{/* Post Details */}
{/* Header */}
<ModalHeader left={toggleModalClose} right={addDetailItem} />
<View style={{flex: 1, backgroundColor: COLORS.primary3, borderTopRightRadius: 35, borderTopLeftRadius: 35}}>
<View style={styles.addDetailText}>
<Text style={styles.textHeader}>Add details</Text>
<Text style={styles.textBody}>Provide the necessary details for your post.</Text>
</View>
<View style={{paddingHorizontal: 10}}>
<FormInput
autoCapitalize='words'
placeholder={'Give it a title'}
onChange={setTitle}
value={title}
/>
<FormInput
autoCapitalize='words'
placeholder={'Category'}
onChange={setCategory}
value={category}
/>
<FormMultiText
autoCapitalize='sentences'
placeholder={'Caption'}
onChange={setCaption}
multiline={true}
numberOfLines={4}
value={caption}
/>
<FormInput
keyboardType='number-pad'
placeholder={'Price (Optional)'}
onChange={setPrice}
value={price}
/>
</View>
<Text style={{color: COLORS.gray, ...FONTS.body5, paddingHorizontal: 10, marginTop: 5, marginLeft: 65}}>Leave this input empty for a "FREE POST".</Text>
</View>
</BottomSheetModal>
<BottomSheetModal
enablePanDownToClose={false}
ref={storeRef}
snapPoints={['100%']}
index={0}
backgroundComponent={
({style}) =>
<View style={[style, {
backgroundColor: COLORS.primary,
borderRadius: 30,
}]}/>}
>
<ModalHeader left={toggleStoreModalClose} right={store} />
<View style={styles.storeItemConatainer}>
<View style={{flex: 1, backgroundColor: COLORS.primary3, borderTopRightRadius: 35, borderTopLeftRadius: 35}}>
<View >
<View style={styles.titleBar}>
<Text style={styles.textHeader}>Add store items (Optional)</Text>
</View>
<Text style={styles.textBody}>Include items available for purchase and their item price. You can add more than one item and multiple photo(s) to each item.</Text>
</View>
{/* Store Items */}
<TouchableOpacity onPress={pickPhoto} style={styles.addPhotoBtn}>
<Text style={{color: COLORS.primary, textAlign: 'center', ...FONTS.body3}}>Add Photo(s)</Text>
</TouchableOpacity>
{/* Photo Container */}
<View style={{flexDirection: 'row'}}>
{Array.from(Array(numColumns)).map((col, colIndex) => (
<View style={{flex: 1}} key={colIndex}>
{photoArray
.filter((item, index) => index % numColumns === colIndex)
.map((item, index) => (
<View key={index.toString()} style={styles.photoContainer}>
<Image source={{uri: item}} style={styles.addedPhoto}/>
<TouchableOpacity style={styles.photoCancel} onPress={() => deletePhoto(index)}>
<Image source={icons.cross} style={styles.photoCancelIcon}/>
</TouchableOpacity>
</View>
))}
</View>
))
}
</View>
<View style={styles.storeInput}>
<TouchableOpacity onPress={addStoreItem}>
<View style={styles.iconContainer}>
<Image source={icons.add} style={styles.icon}/>
</View>
</TouchableOpacity>
<View >
<FormStoreInput
autoCapitalize='words'
placeholder={'Item Name'}
onChange={setItemName}
value={itemName}
/>
<FormStoreInput
keyboardType='number-pad'
placeholder={'Item Price'}
onChange={setItemPrice}
value={itemPrice}
/>
</View>
</View>
<BottomSheetScrollView>
{storeItems.map((item, index) => {
return (
<View key={index.toString()} style={{flexDirection: 'row', marginBottom: 10, marginTop: 5}}>
<AddStoreItem items={item}/>
<TouchableOpacity style={styles.cancel} key={index} onPress={() => deleteItem(index)}>
<Image source={icons.cross} style={styles.cancelIcon}/>
</TouchableOpacity>
</View>
)
})
}
</BottomSheetScrollView>
</View>
</View>
</BottomSheetModal>
</View>
)
}
export default AddPost
This post has been updated.
I guess since we are dealing with images and not an object array that's why index wasnt working. So i changed index with items and decided to us the .filter method instead of .splice. And it worked.
const deleteMedia = (item) => {
setMedia((prev) => prev.filter((el) => el !== item));
console.log(item)
};
You need to declare remove media outside the function delete media because everytime you call delete media. You are reinitalizing the removeMedia useState to the original media values. I did not test these codes, but from the codes I can clearly tell that you are just reinitalizing remove media back to its original value. Try this
let removeMedia=[...media];
const deleteMedia = (index) => {
removeMedia.splice(index, 1);
setMedia(removeMedia)
};
How do I make the text "BIRD" label that will check the checkbox while press on it ?
According to my example it does not work and it is not clear to me.
import CheckBox from '#react-native-community/checkbox';
function App(props) {
const isSelected = useSelector((rootState) => rootState.BitzuaDigumReducer.checkBox);
return (
<TouchableOpacity
activeOpacity={1}
style={{
flexDirection: 'row',
alignSelf: 'flex-start',
top: 20,
left: 10,
}}
>
<CheckBox
value={isSelected}
onValueChange={(value) => dispatch(setCheckBox(value))}
style={styles.checkbox}
tintColors={{ true: 'white', false: 'white' }}
/>
<Text style={styles.label}>BIRD</Text>
</TouchableOpacity>
);
}
I mocked Checkbox on this example, but please review it. It should give you a basic idea on how to toggle checkbox value by pressing the label: https://snack.expo.io/#zvona/toggle-checkbox-on-label
Core code:
const App = () => {
const [isSelected, setSelected] = useState(false);
const toggleCheckbox = () => {
setSelected(!isSelected);
// Here you can dispatch the event to state management
};
return (
<View style={styles.container}>
<Checkbox onValueChange={toggleCheckbox} selected={isSelected} />
<TouchableOpacity activeOpacity={0.8} onPress={toggleCheckbox}>
<Text style={styles.label}>{'Label'}</Text>
</TouchableOpacity>
</View>
);
};
I am working on a hobby gym management app, and I am puzzled by the mechanism of sharing state between three components in React-Native.
My three components are:
1. Schedule:
[...]
function Schedule() {
return (
<Stack.Navigator
initialRouteName="Monday"
screenOptions={{
headerStyle: { backgroundColor: "#f58220" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" },
headerRight: () => <SwitchButton />,
}}
>
<Stack.Screen
name="TabStack"
component={TabStack}
options={{ title: "Aerobic Schedule" }}
/>
</Stack.Navigator>
);
}
export default Schedule;
I want the SwitchButton button in my Schedule component (1.) to alternate between DATA_AEROBIC and DATA_KIDS arrays props of the FlatList in (2.) based on the content of the listAerobic boolean variable.
2. MondayPage:
[...]
const MondayPage = () => {
const [selectedId, setSelectedId] = useState(null);
const [listAerobic, setListAerobic] = useState(true);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, padding: 5 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={listAerobic ? DATA_AEROBIC : DATA_KIDS}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
</SafeAreaView>
</View>
</SafeAreaView>
);
};
However, I don't know how to link the listAerobic boolean variable to the state of the SwitchButton component (3.) , and how to make it toggle on and off.
3. SwitchButton:
const SwitchButton = () => {
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => setIsEnabled(previousState => !previousState);
return (
<View style={styles.container}>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
ios_backgroundColor="#3e3e3e"
onValueChange={toggleSwitch}
value={isEnabled}
/>
<Text> aerobic/kids</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
marginRight: 5,
padding: 5,
}
});
export default SwitchButton;
Any guidance would be awesome! I mention I have really tried to look it up on different tutorials, but I can't seem to get the gist of it. It is my first project in React/React-Native.
Many thanks!
I think you just need 'value' to accept a prop passed into it on the switch button. Then wherever you use switch button just pass a boolean value into it from state e.g.
<SwitchButton enabled={this.state.switchEnabled}/>
As for setting state 'globally' so this.state.switchEnabled can be updated from various places / accessible all over the app you need to look into state management tools like Redux (or I hear 'React Hooks' is now a thing and preferred....)
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>
)
}
}
I am using React Native and React Navigation to build a simple app.
I have got the basic structure working with stub state but I am having problem with changing state via callback and re-render.
In my screen, I have simple start button
`<View style={styles.buttonContainer}>
<TouchableOpacity
style={[myStyles.buttonStyle, { backgroundColor: color }]}
onPress={() => handlePress(button.title)}
>
<Text style={myStyles.textStyle}>{button.title}</Text>
</TouchableOpacity>
</View>`
Problem:
After I update my parent Component state, my child component does not instantly render to match the state change. I understood React will re-render all child components when parent state is changed?
Instead, I have to move back to previous screen and navigate again to my button screen to see that the button's color and text has changed correctly.
I've read about requiring a componentWillReceiveProps(nextProps) handler but I am not sure how to use it. I put a console.log('nextProps', nextProps) inside but it does not get fired.
From navigation perspective, the Root component is on index[0] and my button view is at index[3] so it's the 3rd screen from the root.
EDIT 1: Added Code
myButton screen:
export class TeamsScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params.game.name}: Select Team`,
headerTintColor: 'white',
headerStyle: {
backgroundColor: 'black',
},
headerVisible: true
})
componentWillReceiveProps(nextProps) {
console.log('nextProps', nextProps);
}
render() {
const { navigate, setParams } = this.props.navigation;
const { game, player, setGameState } = this.props.navigation.state.params;
const color = game.status === 'Start' ? 'green' : 'red';
const index = game.indexOf(player);
const status = game.status;
console.log('index', index);
console.log('status', status);
return (
<View style={styles.container}>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[myStyles.buttonStyle, { backgroundColor: color }]}
onPress={() => setGameState(index, status)}
>
<Text style={myStyles.textStyle}>{game.status}</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<Button
onPress={() => navigate('ChangeDriverScreen', { team, game })}
title='Change Driver'
/>
</View>
<View style={{ marginTop: 40, marginBottom: 20 }}>
<Text style={{ fontSize: 16, color: 'white', alignSelf: 'center' }}>Teams</Text>
</View>
<View style={{ height: 250 }}>
<FlatList
data={player.teams}
renderItem={({item}) =>
<View style={styles.buttonContainer}>
<Button
onPress={() => navigate('TeamSelectedStartScreen', { team: item })}
title={item.name}
/>
</View>}
keyExtractor={item => item.name}
/>
</View>
<Image
style={{ alignSelf: 'center', justifyContent: 'flex-end', height: 75, width: 250, resizeMode: 'stretch'}}
source={require('./images/icons/playerPlaceholder.png')}
/>
</View>
)}}
Then the onPress function that is called back:
setGameState = (gameIndex, status) => {
console.log('setGameState', gameIndex, status);
console.log('gameStateBefore', this.state.game);
const newGameState = this.state.game.map(t => {
console.log(this.state.game.indexOf(t));
if (this.state.game.indexOf(t) === gameIndex) {
const newStatus = status === 'Start' ? 'Stop' : 'Start';
t.status = newStatus; /*eslint no-param-reassign: "off"*/
console.log('inside if', t.status);
console.log('inside if game', t);
return t;
}
return t;
});
console.log('new Game State', newGameState);
this.setState(() => ({
game: newGameState
}));
}
So the setState method works (as re-navigating back to screen 3 shows the correct state but core question is how to get immediate re-render of screen 3 when setState is called from Screen 0.