Checkbox will not render on state update in React Native - javascript

I am trying to get a specific checkbox to check and uncheck.
This is the parent component:
import React, { Component } from 'react';
import { View, Text, Modal, TouchableHighlight } from 'react-native'
import { loadLeagues } from '../actions'
import { connect } from 'react-redux'
import Check from './CheckBox'
class LeagueSelect extends Component {
constructor(props) {
super(props)
this.state = {
modalVisible: false,
checked: []
}
}
// state = {
// modalVisible: false,
// checked: []
// }
setModalVisible(visible) {
this.setState({modalVisible: visible})
this.props.league.map(
(v, i) => {
return this.state.checked.push(false)
}
)
}
componentDidMount() {
this.props.loadLeagues()
}
changeCheck = (index) => {
newChecked = this.state.checked.splice(index, 1, !this.state.checked[index])
console.log('newChecked', this.state.checked)
this.setState({ checked: newChecked })
console.log('league checked state', this.state.checked)
}
render() {
return (
<View style={{marginTop: 22}}>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}
>
<View style={{marginTop: 100}}>
<TouchableHighlight
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
<Text>Leagues</Text>
{this.props.league === null ?'' : this.props.league.map(
(v, i) => {
return(
<View>
<Check
checked={this.state.checked[i]}
index={i}
changeCheck={this.changeCheck}
/>
<Text>{v.acronym}</Text>
</View>
)
}
)}
</View>
</Modal>
<TouchableHighlight
onPress={() => {
this.setModalVisible(true);
}}>
<Text>Show Modal</Text>
</TouchableHighlight>
</View>
);
}
}
function mapStateToProps(state) {
return {
league: state.league.league
}
}
export default connect(mapStateToProps, { loadLeagues })(LeagueSelect)
This is the child component that I'm passing the index of the the checkbox into to update; I'm also passing in the function that updates the state of the checkbox at the specific index:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { CheckBox } from 'native-base'
export default class Check extends Component {
constructor(props) {
super(props)
this.state = {
cards: []
}
}
localChange = () => {
this.props.changeCheck(this.props.index)
}
render() {
return(
<CheckBox
checked={this.props.checked}
onPress={this.localChange}
/>
)
}
}
I can see that the state updates when press the checkbox through the console logs that I have setup, but the checkbox isn't updating based off of the new state.

Related

React Native Flatlist ListItem called many times after initial render

Flatlist component as below
<FlatList
data={this.state.data}
keyExtractor={item => item.ID.toString()}
renderItem={this.renderItem}
onRefresh={this.onRefresh}
refreshing={this.state.refreshing}
ListFooterComponent={this.renderFooter}
/>
renderItem = ({ item }) => {
return (
<ListElement onPress={() => this.rowData(item)} item={item}/>
);
};
ListItem Component
import React from "react";
import { Image } from "react-native";
import { Left, Right, Body, Text, ListItem } from "native-base";
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from "react-native-responsive-screen";
import Thumbnails from "../components/Thumbnails";
import styles from "../styles/HomeStyles";
import GlobalStyles from "../constants/GlobalStyles";
class ListElement extends React.Component {
componentDidMount(){
console.log(this.props.item.ID)
}
shouldComponentUpdate(){
return false;
}
render() {
return (
<ListItem onPress={this.props.onPress} thumbnail style={styles.listItem}>
<Left>
<Thumbnails imageURI={require("../../assets/images/female2.png")} />
</Left>
<Body style={{ borderBottomColor: "transparent", top: 2 }}>
<Text
style={{
fontFamily: GlobalStyles.primaryFont,
fontSize: hp("1.85%"),
}}
>
{this.props.item.FirstName} {this.props.item.LastName}
</Text>
<Text
note
style={{
fontFamily: GlobalStyles.primaryFont,
color: "#666666",
fontSize: hp("1.6%"),
}}
>
{this.props.item.Title}
</Text>
</Body>
<Right>
<Image
style={styles.rightArrow}
source={require("../../assets/images/arrowRight.png")}
/>
</Right>
</ListItem>
);
}
}
export default ListElement;
I tried to populate api data on the flatlist. Please refer my above code for implementation. Currently I am facing rerendering issues as mentioned below.
My listitem componentDidMount is invoking multiple times on scroll after even intial render of all listitems.
I have tried PureComponent and shouldComponentUpdate. Thanks in advance.
An Update
Please Find the entire code of Flatlist component
import React, { PureComponent } from "react";
import { View, FlatList } from "react-native";
import { ListItem } from "react-native-elements";
import FL from "../screens/FL";
import FL1 from "../screens/Fl1";
class FlatListDemo extends PureComponent {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
refreshing: false,
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://jsonplaceholder.typicode.com/posts`;
this.setState({ loading: true });
fetch(url)
.then((res) => res.json())
.then((res) => {
this.setState({
data: res,
error: res.error || null,
loading: false,
refreshing: false,
});
})
.catch((error) => {
this.setState({ error, loading: false });
});
};
renderItem = ({ item }) => {
return <ListElement onPress={() => this.rowData(item)} item={item} />;
};
render() {
if (this.state.loading === true) {
return <View></View>;
} else {
return (
<View>
<FlatList
data={this.state.data}
keyExtractor={(item) => item.ID.toString()}
renderItem={this.renderItem}
onRefresh={this.onRefresh}
refreshing={this.state.refreshing}
ListFooterComponent={this.renderFooter}
/>
</View>
);
}
}
}
export default FlatListDemo;
Try the following:
add to state a boolean value (you can name it "wasFetched").
Then change the componentDidMount like this:
componentDidMount() {
if(this.state.wasFetched === false) {
this.setState({ wasFetched: true }, () => this.makeRemoteRequest())
}
}

React native - Invariant violation-tried to get frame for out of range index

I have tried to search other posts and forums for the solution to this error. However, either no one has solved those issues or the issues are not really doing the same thing as I am doing. I am getting an error saying " Invariant violation-tried to get frame for out of range index". This happens when I try to input the data into the flatlist from the poke api. Please see code below.
import React, { useState } from "react";
import { View, Text , Button, FlatList, ActivityIndicator } from "react-native";
import { GlobalStyles } from "../styles/GlobalStyles";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: []
}
}
componentDidMount() {
fetch("https://pokeapi.co/api/v2/pokemon/")
.then((res)=> res.json())
.then((response)=> {
this.setState({
isLoading: false,
dataSource: response
})
console.log(response)
})
}
render() {
const showIndicator = this.state.isLoading == true ? <ActivityIndicator size="large" color="#0000ff" /> : null;
return(
<View style={GlobalStyles.container}>
<View style={GlobalStyles.activityIndicator}>{showIndicator}</View>
<FlatList data={this.state.dataSource} renderItem={({item})=> {
return(
<View>
<Text>{item.name}</Text>
</View>
)
}}/>
<Button onPress={()=> this.props.navigation.navigate("About")} title="Go to about"/>
</View>
)
}
}
export default Home;
The thing you are doing wrong is you are sending this.state.datasource is your data attribute, you need to send this.state.dataSource.results
import React, { useState } from "react";
import { View, Text , Button, FlatList, ActivityIndicator } from "react-native";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: []
}
}
componentDidMount() {
fetch("https://pokeapi.co/api/v2/pokemon/")
.then((res)=> res.json())
.then((response)=> {
this.setState({
isLoading: false,
dataSource: response
})
console.log(response)
})
}
render() {
const showIndicator = this.state.isLoading == true ? <ActivityIndicator size="large" color="#0000ff" /> : null;
return(
<View style={{marginTop:100}}>
<View>{showIndicator}</View>
<FlatList data={this.state.dataSource.results}
renderItem={({item})=> {
console.log("item is item",item);
return(
<View>
<Text>{item.name}</Text>
</View>
)
}}/>
<Button onPress={()=> this.props.navigation.navigate("About")} title="Go to about"/>
</View>
)
}
}
export default Home;
Hope this helps!

React state passes from one component to the next inconsistently

I have two components: LeagueSelect and TeamSelect.
All I'm trying to do right now is pass the checkedLeagues state from LeagueSelect to TeamSelect.
It's currently setup to have the checkboxes in TeamSelect be checked if the corresponding league is checked.
The issue: the state passes from LeagueSelect to TeamSelect inconsistently.
This is a video of what it looks like:
https://streamable.com/2i06g
When a box is unchecked, the state updates 'in team', as you can see in the console.log, but, when you try to check the same box again the state does not update in team.
I initially tried to implement this with redux, thought this issue was a redux issue, moved to directly passing state to the child component, and realized that the issue must be somewhere else.
This is my LeagueSelect component:
import React, { Component } from 'react';
import { View, Text, Modal, TouchableHighlight, FlatList, Button } from 'react-native'
import { loadLeagues } from '../actions'
import { connect } from 'react-redux'
import Check from './CheckBox'
import axios from "axios"
import { loadCards, loadTeams, changeLeagues } from '../actions'
import { Icon } from 'native-base'
import TeamSelect from './TeamSelect'
class LeagueSelect extends Component {
constructor(props) {
super(props)
this.state = {
modalVisible: false,
checked: [],
checkedLeagues: [],
checkMessage: ''
}
}
setModalVisible(visible) {
this.setState({modalVisible: visible})
if(this.state.checked.length === 0) {
this.props.league.map(
(v, i) => {
this.state.checked.push(true)
this.state.checkedLeagues.push(v.acronym)
}
)
}
this.setState({ checkMessage: '' })
}
changeCheck = (index, acronym) => {
//local variable to create query param
firstString = []
//adds to local variable if not checked, takes out variable if checked
if(!this.state.checkedLeagues.includes(acronym)) {
firstString.push(acronym)
} else {
firstString.filter(v => { return v !== acronym})
}
//adds leagues that could be in the current state that were not just passed in
this.state.checkedLeagues.map(
(v, i) => {
if(v !== acronym) {
firstString.push(v)
}
}
)
//updates checked leagues state
//makes api call with parameters set
//prevents all leagues being unselected
if(acronym === this.state.checkedLeagues[0] && firstString.length === 0) {
this.setState({ checkMessage: `Don't you want something to look at?` })
} else {
if(!this.state.checkedLeagues.includes(acronym)){
this.state.checkedLeagues[this.state.checkedLeagues.length] = acronym
this.setState({ checkedLeagues: this.state.checkedLeagues })
} else {
newChecked = this.state.checkedLeagues.filter(v => { return v !== acronym})
this.setState({checkedLeagues: newChecked})
}
//updating the check
this.state.checked[index] = !this.state.checked[index]
this.setState({ checked: this.state.checked })
queryString = []
firstString.map(
(v, i) => {
if (queryString.length < 1) {
queryString.push(`?league=${v}`)
} else if (queryString.length >= 1 ) {
queryString.push(`&league=${v}`)
}
}
)
axios.get(`http://localhost:4000/reports${queryString.join('')}`)
.then(response => {
this.props.loadCards(response.data)
})
}
}
render() {
return (
<View style={{ position: 'relative'}}>
<Text
style={{
paddingTop: 8,
paddingLeft: 5,
fontSize: 15
}}
>Leagues</Text>
<View
style={{
flexDirection:"row",
}}
>
{this.props.league === null ?'' : this.props.league.map(
(v, i) => {
return(
<View
key={i}
style={{
alignSelf: 'flex-end',
flexDirection:"row",
top: 4,
}}
>
<Check
checked={this.state.checked[i]}
index={i}
value={v.acronym}
changeCheck={this.changeCheck}
/>
<Text
style={{
paddingLeft: 23,
}}
>{v.acronym}</Text>
</View>
)
}
)}
</View>
<Text
style={{
paddingLeft: 10,
paddingTop: 12,
fontStyle: 'italic',
color: '#F4AF0D'
}}
>{this.state.checkMessage}</Text>
<TeamSelect checkedLeagues={this.state.checkedLeagues}/>
</View>
</View>
);
}
}
export default LeagueSelect
This is my TeamSelect component:
import React, { Component } from 'react'
import { Text, View } from 'react-native'
import { connect } from 'react-redux'
import { loadTeams, loadLeagues } from '../actions'
import Check from './CheckBox'
class TeamSelect extends Component {
constructor(props) {
super(props)
this.state = {
checked: [],
checkedTeams: [],
setOnce: 0
}
}
render() {
console.log('in team', this.props.checkedLeagues)
return(
<View>
{
this.props.team === null ?'' : this.props.team.map(
(v, i) => {
return(
<View key={i}>
<Check
checked={ this.props.checkedLeagues.includes(v.league.acronym) ? true : false }
index={i}
value={v.team_name}
changeCheck={this.changeCheck}
/>
{ v.team_name === undefined ? null :
<Text>{v.team_name}</Text>}
</View>
)
}
)
}
</View>
)
}
}
export default TeamSelect
this.setState({ checkedLeagues: this.state.checkedLeagues })
Statements like these can cause issues as you are mutating and setting the state to the same object. The reference to checked leagues doesn't get updated and react may not trigger a render. Use this instead
this.setState({ checkedLeagues: [...this.state.checkedLeagues] })
But this whole approach to the problem is wrong, you should use one leagues object that has a checked property to it, and pass it down.
make you league object look like this,
const leagues = [
{
acronym: 'abc',
checked: false,
teams: [ ...array of teams here ]
},
...
]
When you pass it down to TeamSelect, you can map it like this
const { leagues } = this.props
{leagues && leagues.map((league, i) => league.teams.map((team, j) (
<View key={team.team_name}>
<Check
checked={league.checked}
index={i + i * j}
value={team.team_name}
changeCheck={() => this.changeCheck(i, j)}
/>
{team.team_name && <Text>{team.team_name}</Text>}
</View>)))}
Same with leagueSelect, you can map leagues like this:
const { leagues } = this.state
{leagues.map((league, i) => (
<View
key={league.acronym}
style={{
alignSelf: 'flex-end',
flexDirection:"row",
top: 4,
}}>
<Check
checked={league.checked}
index={i}
value={league.acronym}
changeCheck={this.changeCheck}
/>
<Text
style={{
paddingLeft: 23,
}}
>{league.acronym}</Text>
</View>
)
)}
Note: leagues have to be copied from props to state for you to mutate it. I just typed this so it will need some changes before it runs, it's just meant to show you the "react way" of coding this.
https://reactjs.org/docs/lifting-state-up.html

React Native - Modal with Flatlist items

I'm making a modal that will popup when the user clicks a flatlist button or items, and there the user will see the details about the item he/she clicks. Basically, I want to pass the items of flatlist to modal.
I'm actually done with the popup of the modal, now I have to show the details like menu description and menu price. I've found a post here in stackoverflow and I follow everything in there but I am having an error regarding with an " id ", and I can't figure out how to fix it.
Here is my code
Details.js
import React, {Component} from 'react';
import {Text, TouchableHighlight, View,
StyleSheet, Platform, FlatList, AppRegistry,
TouchableOpacity, RefreshControl, Dimensions, Modal, TextInput, TouchableWithoutFeedback, Keyboard
} from 'react-native';
import AddModal from '../Modal/AddModal';
var screen = Dimensions.get('window');
const DismissKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress = {() => Keyboard.dismiss()}>
{children}
</TouchableWithoutFeedback>
);
export default class Details extends Component {
static navigationOptions = {
title: ''
};
constructor()
{
super ()
this.state = {
data: [],
showModal: false,
id: null,
}
}
fetchData = async() => {
const { params } = this.props.navigation.state;
const response_Cat = await fetch('http://192.168.254.101:3307/categories/' + params.id);
const category_Cat = await response_Cat.json();
this.setState({data: category_Cat});
};
componentDidMount() {
this.fetchData();
};
_onRefresh() {
this.setState({ refreshing: true });
this.fetchData().then(() => {
this.setState({ refreshing: false })
});
};
_onPressItem(id) {
this.setState({
modalVisible: true,
id: id
});
}
_renderItem = ({item}) => {
return (
<TouchableHighlight
id = { item.menu_desc }
onPress = { this._onPressItem(item.menu_desc) }
>
<View>
<Text>{ this.state.id }</Text>
</View>
</TouchableHighlight>
)
};
render() {
const { params } = this.props.navigation.state;
return (
<View style = { styles.container }>
<AddModal
modalVisible = { this.state.modalVisible }
setModalVisible = { (vis) => { this.setModalVisible(vis) }}
id = { this.state.id }
/>
<FlatList
data = { this.state.data }
renderItem = { this._renderItem }
keyExtractor={(item, index) => index}
/*refreshControl = {
<RefreshControl
refreshing = { this.state.refreshing }
onRefresh = { this._onRefresh.bind(this) }
/>
}*/
/>
</View>
);
}
}
const styles = StyleSheet.create({
...
})
//AppRegistry.registerComponent('Details', () => Details);
AddModal.js
import React, {Component} from 'react';
import {Text, TouchableHighlight, View,
StyleSheet, Platform, FlatList, AppRegistry,
TouchableOpacity, RefreshControl, Dimensions, TextInput, Modal
} from 'react-native';
export default class AddModal extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
id: null
};
}
componentWillReceiveProps(nextProps) {
this.setState({
showModal: nextProps.showModal,
id: nextProps.id,
})
}
render() {
return (
<Modal
animationType="slide"
transparent={ true }
visible={ this.state.modalVisible }
onRequestClose={() => { this.props.setModalVisible(false) }}>
<View>
<View>
<Text>{ this.state.id }</Text>
<TouchableHighlight
onPress = {() => { this.props.setModalVisible(false) }}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
)
}
}
Just wanted to pointout an issue in your code (not related to 'id' error, id error already answer by digit). In the renderItem function, you are calling onPress = { this._onPressItem(item.menu_desc) }, it should be changed to
onPress = { () => this._onPressItem(item.menu_desc) }
I guess, you will call the onPressItem when user click on list item.
EDIT:
I have made a couple of changes to make your code working. Here are the changes.
In your AppModal.js, you are setting modal visibility in showModal: nextProps.showModal , but in the <Modal ...> you have set visible
= { this.state.modalVisible }. Also in Details.js you have written <AddModal modalVisible ...>.
First I changed showModal to modalVisible in Details.js and in AppModal.js.
Details.js
constructor()
{
super ()
this.state = {
data: [],
modalVisible: false,
id: null,
}
}
Then I changed _onPressItem(id) to _onPressItem = (id)
Made changes in renderItem as
<TouchableHighlight
id = { item.enName }
onPress = { () => this._onPressItem(item.enName) }
>
in render function I have set modal visibility as
<AddModal
...
setModalVisible = { (vis) => { this.setState({
modalVisible: vis
})
}}
...
/>
AppModal.js
Changed showModal to modalVisible
constructor(props) {
super(props);
this.state = {
modalVisible: props.modalVisible,
d: null
};
}
componentWillReceiveProps(nextProps) {
this.setState({
modalVisible: nextProps.modalVisible,
id: nextProps.id,
})
}
In the constructor, I have added modalVisible: props.modalVisible.
Hope this will help!
I guess item.menu_desc is an id of each item so it must be {item.menu_desc} not {id}. Change it like below
_renderItem = ({item}) => {
return (
<TouchableHighlight
id = { item.menu_desc }
onPress = { this._onPressItem(item.menu_desc) }
>
<View>
<Text>{ item.menu_desc }</Text>
</View>
</TouchableHighlight>
)
};

Displaying props in a second target box

I'm very new to react-native. I'm currently experimenting with it to figure out how I can use it in different ways. Presently, I'm trying to call props of a specifically tapped object and send them to an output box.
So when you tap 'Alan' or 'Steve' their name will appear in the red box.
I'd also like for the dark blue backkground to change to dark red once it's tapped?
I have had a good read of the docs, but I reckon I'm not getting it because it's new to me. I know that I don't seem to be able to access the props of Component which is obviously the class Obj extends
Guidance greatly appreciated.
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import style from './style';
class Obj extends Component {
render(){
return(
<TouchableOpacity
onPressIn={() => this.setState({tapped: true, tappedName: this.props.plrName})}
onPressOut={() => this.setState({tapped: false, tappedName: null})}>
<View style={[style.playerobject, style.shadow]}>
<Text style={[style.plrobjText]}>{this.props.plrName}</Text>
</View>
</TouchableOpacity>
)
}
}
export default class App extends Component {
constructor(props){
super(props);
this.state = {
tapped: false,
tappedName: null,
};
}
render() {
return (
<View style={[style.main]}>
<View style={[style.container, this.state.tapped ? {backgroundColor:'darkred'} : {backgroundColor:'darkblue'} ]}>
<Obj plrName='Alan' />
<Obj plrName='Steve' />
</View>
<View style={style.box }><Text>|{this.state.tapped ? this.state.tappedName : 'x'}|</Text></View>
</View>
);
}
}
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import style from './style';
class Obj extends Component {
onPressIn = () => {
this.props.onPressIn(this.props.plrName)
}
onPressOut = () => {
this.props.onPressOut()
}
render() {
return (
<TouchableOpacity
onPressIn={this.onPressIn}
onPressOut={this.onPressOut}>
<View style={[style.playerobject, style.shadow]}>
<Text style={[style.plrobjText]}>{this.props.plrName}</Text>
</View>
</TouchableOpacity>
)
}
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
tapped: false,
tappedName: null,
};
}
onPressIn = (tappedName) => {
this.setState({ tapped: true, tappedName })
}
onPressOut = () => {
this.setState({ tapped: false, tappedName: null })
}
render() {
return (
<View style={[style.main]}>
<View style={[style.container, this.state.tapped ? { backgroundColor: 'darkred' } : { backgroundColor: 'darkblue' }]}>
<Obj plrName='Alan' onPressIn={this.onPressIn} onPressOut={this.onPressOut} />
<Obj plrName='Steve' onPressIn={this.onPressIn} onPressOut={this.onPressOut} />
</View>
<View style={style.box}><Text>|{this.state.tapped ? this.state.tappedName : 'x'}|</Text></View>
</View>
);
}
}

Categories

Resources