I know how to add and remove a single component by changing the state. But this way wont work if you have multiple components to remove. For instance lets say I have 3 Views. How can I remove them when I click on them.
Example code:
class Example extends Component {
render(){
return (
<View>
<View>
<TouchAbleOpacity onPress={() => this.removeView()}>
<Text>Remove View 1</Text>
</TouchAbleOpacity>
</View>
<View>
<TouchAbleOpacity onPress={() => this.removeView()}>
<Text>Remove View 2</Text>
</TouchAbleOpacity>
</View>
<View>
<TouchAbleOpacity onPress={() => this.removeView()}>
<Text>Remove View 3</Text>
</TouchAbleOpacity>
</View>
</View>
)
}
removeView(){
}
}
Another example will be when I have a ListView with buttons inside. These are buttons to invite a user. When I click on the button I want to hide the button for that specific row in the ListView.
Any suggestions?
Thanks to Giorgos I found a solution for my own question. I created a separate component with a hide function inside the component. Now I can just add this component anywhere in a view or in a listView and when I click on it it will hide. Remember this only hides the component and does not unmount it.
This is just an example so I created a button component.
My Button Component:
class ButtonComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
hide:false
}
}
render() {
return (
<View style={styles.container}>
{this.renderButtonComponent()}
</View>
);
}
renderButtonComponent(){
if(!this.state.hide){
return (
<TouchableOpacity onPress={this.hide.bind(this)}>
<Text>Button</Text>
</TouchableOpacity>
);
}
}
hide(){
this.setState({
hide:true
});
}
}
In my View I just render my Component:
render() {
return (
<View style={styles.container}>
<ButtonComponent/>
<ButtonComponent/>
<ButtonComponent/>
</View>
);
}
You have to use your component's state. Whenever you call setState the component's render() function is triggered again. There based on what the current state is, you can decide what to show and what not. For example:
class Example extends Component {
constructor(props){
// initialize state
this.state = {
isView1Visible: true,
isView2Visible: true,
isView2Visible: true
}
}
render(){
return (
<View>
{ this.renderView1() }
{ this.renderView2() }
{ this.renderView3() }
</View>
)
}
renderView1(){
if(this.state.isView1Visible){
return (
<View>
<TouchAbleOpacity onPress={() => this.setState( {isView1Visible: false} )}>
<Text>Remove View 1</Text>
</TouchAbleOpacity>
</View>
)
}
renderView2(){
if(this.state.isView2Visible){
return (
...
)
}
renderView3(){
if(this.state.isView3Visible){
return (
...
)
}
}
In the above example, you render your view based on the current state. When the button is clicked, you update the state by calling setState() which, like I mentioned before, will trigger another call to render().
With the ListView the approach is the same but the implementation slightly different. What you need to do there is to save your list of items in the state of your component and whenever you want to add/remove an item, you update the list accordingly and then update the state using setState. For example, something similar to this:
constructor(props) {
super(props);
var list = [ ... ]
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
dataSource: ds,
items: ds.cloneWithRows(list)
};
}
render() {
return (
<View>
<ListView
dataSource={this.state.items}
renderRow={(rowData) => this.renderRow(rowData) /> } />
</View>
)
}
renderRow(rowData) {
<View>
<TouchAbleOpacity onPress={() => this.updateList()}>
<Text>Remove View 1</Text>
</TouchAbleOpacity>
</View>
}
updateList() {
// do some changes to your list and update the state.
var newItems = ...
this.setState({
items: newItems
})
}
Hope this helps.
Related
I want to check if data exist, If data exist then to show red button and if there is no data then it will show blue button. I tried to do that but its not working . For example Instagram go to DM's button if there are any DM , button change to different color with number of DM's unread and if there is no Dm's then it is simple button.
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
const { notifications } = this.props.notification;
return {
headerRight: (
<View>
{
(notifications.length > 0) ? (
<TouchableOpacity onPress={() => params.handleSave()}>
<Badge>
<Text>{notifications.length}</Text>
</Badge>
</TouchableOpacity>
) : (
<TouchableOpacity onPress={() => params.handleSave()}>
<Ionicons
name="ios-notifications-outline"
style={{ paddingRight: 15 }}
size={24}
color="white"
/>
</TouchableOpacity>
)
}
</View>
)
};
}
create two different styles and condition
style={[navigation.state.yourstate ? styles.selectedButtonStyle : styles.normalButtonStyle]}
I'm fetching data then saving them in a sqlite file on the mobile. When this screen is called the fetch code begins called by componentDidMount() function. When the fetch is over this does a setState() thus calls componenDidUpdate() function (saving the data in sqlite then setState= 'isLoadingDataProgramadas: false') so I want the application to make a transition to 'ActProgramadas' screen with React-Navigation V3. The solution below works, but gets me a warning:
Warning: Cannot update during an existing state (such as within
'render'). Render methods should be a pure function of props and
state.
How can I fix that?
componentDidMount() {
this.doFetch();
}
componentDidUpdate(){
this.openBD();
this.insertSQLITE();
}
componentWillUnmount() {
this.closeDatabase();
}
render() {
if(this.state.isLoadingDataProgramadas)
return (
<View style={stylesLoading.container}>
<View>
<ActivityIndicator size="large" color="lightblue"/>
</View>
<View>
<Text style={stylesLoading.texto}>
Descargando datos...
</Text>
</View>
</View>
);
else
return(
<View>
{this.props.navigation.navigate('ActProgramadas')}
</View>
);
}
Probably navigation.navigate uses under the hood, a setState function. My suggestion is to make a function to encapsulate the navigation into a setTimeout function like that
navigate = () => {
const { navigation } = this.props
setTimeout(() => {
navigation.navigate('ActProgramadas')
}, 1000);
}
...
<View>
{this.navigate()}
</View>
Its looks like a typo in Your if else statement.
And try to bind Your navigation event handler like this or do it in constructor.
Try this:
render() {
if(this.state.isLoadingDataProgramadas) {
return (
<View style={stylesLoading.container}>
<View>
<ActivityIndicator size="large" color="lightblue"/>
</View>
<View>
<Text style={stylesLoading.texto}>
Descargando datos...
</Text>
</View>
</View>
);
} else {
return(
<View>
{() => this.props.navigation.navigate('ActProgramadas')}
</View>
);
}
}
I'm making a list view were I will view a list of some data from my database. But after running the program all I got is white background screen. Does anyone knows the solution?
screen shot
Here is my code
export default class Pasta extends Component {
constructor() {
super()
this.state = {
dataSource: []
}
}
renderItem = ({ item }) => {
return (
<View style = {{flex: 1, flexDirection: 'row'}}>
<View style = {{flex: 1, justifyContent: 'center'}}>
<Text>
{item.menu_desc}
</Text>
<Text>
{item.menu_price}
</Text>
</View>
</View>
)
}
componentDidMount() {
const url = 'http://192.***.***.***:9090/menu'
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson.menu
})
})
}
render() {
return (
<View style = { styles.container }>
<FlatList
data = { this.state.dataSource }
renderItem = {this.renderItem}
/>
</View>
);
}
}
Add extraData prop to your FlatList to cause a re-render
keyExtractor = (item, index) => item.id; // note: id is the unique key for each item
render() {
return (
<FlatList
data = {this.state.dataSource}
renderItem = {this.renderItem}
extraData={this.state}
keyExtractor={this.keyExtractor}
/>
);
}
Also log and verify your data is present. I suggest referring to FlatList docs for more props like keyExtractor etc.
I have a flat list that renders several buttons, I want to mark the selected button inside esee flat list, I tried a lot of things but in all of them the flat list mark all the items once I select one, besides that, it does not update until i Update something in the component and save the changes, then it does hot reloading and this is how the marked items are displayed
constructor(props) {
super(props);
this.state = {
pressStatus: false,
};
_onShowUnderlay(){
this.setState({ pressStatus: true });
}
render() {
return (
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.ninosPicker}
renderItem={({item}) => (
<View style={styles.hijos}>
<TouchableHighlight
activeOpacity={1}
underlayColor="#3fa9f5"
onShowUnderlay={this._onShowUnderlay.bind(this)}
style={this.state.pressStatus ? styles.buttonPress : styles.botonsito }
onPress={() => this.setHijo(item.grado, item.grupo)}>
<Text style={this.state.pressStatus ? styles.welcomePress : styles.titBtnGyG }>{item.name}</Text>
</TouchableHighlight>
<TouchableHighlight
activeOpacity={1}
underlayColor="#3fa9f5"
style={this.state.pressStatus ? styles.gradosPress : styles.grados }>
<Text style={this.state.pressStatus ? styles.welcomePress : styles.titBtnGyG }>{item.grado}</Text>
</TouchableHighlight>
<TouchableHighlight
activeOpacity={1}
underlayColor="#3fa9f5"
style={this.state.pressStatus ? styles.grupoPress : styles.grupo }>
<Text style={this.state.pressStatus ? styles.welcomePress : styles.titBtnGyG }>{item.grupo}</Text>
</TouchableHighlight>
</View>
)}/>
You have to take into account that in the coding you are placing a unique state, in this case this.state.pressStatus, what you need is to have in the arrangement in each object a property pressStatus, at the time of executing the action of the button you have to change the state of that button
Working with a ListView in React-Native, I have seen that is not the same, moving props to the list item,
Pass functions as props only with the reference, and invoke the parameters in the child component, or
Pass functions as props with parameters defined, and invoke the function with no parameters in the child
None of the solutions works.
The function invoked are Actions creators of Redux, and dispatched. Is this a issue of Redux or React-Native (maybe ReactJS)
This is a snippet, market as //ERROR the code lines that does'nt work followed by the good ones
class App extends Component {
// On props
// data: an Array
// doThis: an action creator of Redux
// doThat: idem
constructor(){
super();
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
}
render () {
const dataSource = this.ds.cloneWithRows(this.props.data);
return (
<View>
<ListView style={{flex:1}}
dataSource={dataSource}
renderRow={(rowData, sectionID, rowID) =>
<Item rowData={rowData}
//ERROR
//onPress={this.props.doThis}
//onLongPress={this..props.doThat}
//RIGHT NO ERROR TOO
onPress={() => this.props.doThis(rowData)}
onLongPress={() => this.props.doThat(rowData)}
/>
}
/>
</View>
)
}
}
class Item extends Component {
render() {
return (
<View>
<TouchableHighlight
//ERROR
//onPress={() => { this.props.onPress( this.props.rowData ) }}
//onLongPress={() => { this.props.onLongPress( this.props.rowData ) }}
//WRONG TOO
onPress={this.props.onPress}
onLongPress={this.props.onLongPress}
>
<Text>
{rowData}
</Text>
</TouchableHighlight>
</View>
);
}
}
There is a repo with this problem here https://github.com/srlopez/test
Thanks in advance
If your high-level callbacks accept a parameter, you need to make sure your anonymous functions accept a parameter as well (Note: creating anonymous functions using the arrow syntax automatically binds our function to the value of this in the current context). I think you witnessed a combination of issues where either your callbacks were bound to the incorrect context (the window) or you weren't accepting the passed arguments:
class App extends Component {
// On props
// data: an Array
// doThis: an action creator of Redux
// doThat: idem
constructor(){
super();
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
}
render () {
const dataSource = this.ds.cloneWithRows(this.props.data);
return (
<View>
<ListView style={{flex:1}}
dataSource={dataSource}
renderRow={(rowData, sectionID, rowID) =>
<Item rowData={rowData}
onPress={(data) => this.props.doThis(data)}
onLongPress={(data) => this.props.doThat(data)} />
}/>
</View>
)
}
}
class Item extends Component {
render() {
return (
<View>
<TouchableHighlight
onPress={() => this.props.onPress(this.rowData)}
onLongPress={() => this.props.onLongPress(this.rowData)}>
<Text>
{rowData}
</Text>
</TouchableHighlight>
</View>
);
}
}
It's probably be a problem with your this not being set right in your closure.
Try binding it this way:
class Item extends Component {
render() {
return (
<View>
<TouchableHighlight
onPress={this.props.onPress.bind(this, this.props.rowData)}
onLongPress={this.props.onLongPress.bind(this, this.props.rowData)}
>
<Text>
{rowData}
</Text>
</TouchableHighlight>
</View>
);
}
}