React-native ListView with Navigation - javascript

I am trying to use component ListView with Navigation but I get undefined is not an object (evaluating'_this.props.navigator') error
My ListView is in Contacts.js
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, ListView, Text, View, Navigator } from 'react-native';
import Row from './Row'
import SearchBar from './SearchBar'
import Sections from './Sections'
import Load from './Load'
import demoData from './data'
import Pagination from '../Pagination';
export default class Contacts extends React.Component{
constructor(props) {
super(props)
const getSectionData = (dataBlob, sectionId) => dataBlob[sectionId];
const getRowData = (dataBlob, sectionId, rowId) => dataBlob[`${rowId}`];
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged : (s1, s2) => s1 !== s2,
getSectionData,
getRowData,
});
const { dataBlob, sectionIds, rowIds } = this.formatData(demoData);
// Init state
this.state = {
dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIds, rowIds),
left: true,
center: false,
right: false
}
}
formatData(data) {
// We're sorting by alphabetically so we need the alphabet
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
// Need somewhere to store our data
const dataBlob = {};
const sectionIds = [];
const rowIds = [];
// Each section is going to represent a letter in the alphabet so we loop over the alphabet
for (let sectionId = 0; sectionId < alphabet.length; sectionId++) {
// Get the character we're currently looking for
const currentChar = alphabet[sectionId];
// Get users whose first name starts with the current letter
const users = data.filter((user) => user.name.first.toUpperCase().indexOf(currentChar) === 0);
// If there are any users who have a first name starting with the current letter then we'll
// add a new section otherwise we just skip over it
if (users.length > 0) {
// Add a section id to our array so the listview knows that we've got a new section
sectionIds.push(sectionId);
// Store any data we would want to display in the section header. In our case we want to show
// the current character
dataBlob[sectionId] = { character: currentChar };
// Setup a new array that we can store the row ids for this section
rowIds.push([]);
// Loop over the valid users for this section
for (let i = 0; i < users.length; i++) {
// Create a unique row id for the data blob that the listview can use for reference
const rowId = `${sectionId}:${i}`;
// Push the row id to the row ids array. This is what listview will reference to pull
// data from our data blob
rowIds[rowIds.length - 1].push(rowId);
// Store the data we care about for this row
dataBlob[rowId] = users[i];
}
}
}
return { dataBlob, sectionIds, rowIds };
}
render() {
return (
<View style={styles.contactsContainer} >
<Pagination
left={this.state.left}
center={this.state.center}
right={this.state.right}
/>
<ListView
style={styles.listContainer}
dataSource={this.state.dataSource}
renderRow={(data) => <Row {...data} navigator={this.props.Navigator} />}
renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator} />}
renderHeader={() => <SearchBar />}
renderFooter={() => <Load />}
renderSectionHeader={(sectionData) => <Sections {...sectionData} />}
/>
</View>
);
}
}
const styles = StyleSheet.create({
contactsContainer: {
flex: 1
},
listContainer: {
marginTop: 10
},
separator: {
flex: 1,
height: StyleSheet.hairlineWidth,
backgroundColor: '#8E8E8E',
}
});
Line
renderRow={(data) => }
calls Row.js and prints contacts data
import React from 'react';
import ProfileScreen from './ProfileScreen'
import { AppRegistry, View, Text, StyleSheet, TouchableHighlight, Alert, Navigator } from 'react-native';
const Row = (props) => (
<TouchableHighlight onPress={() => this.props.Navigator.push({ screen: 'ProfileScreen' })} underlayColor="#FFFFFF">
<View style={ styles.container }>
<View style={ styles.status } />
<Text style={ styles.text }>
{`${ props.name.first } ${ props.name.last }`}
</Text>
</View>
</TouchableHighlight>
);
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 12,
flexDirection: 'row',
alignItems: 'center',
},
text: {
marginLeft: 12,
fontSize: 16,
},
status: {
backgroundColor: '#5cb85c',
height: 10,
width: 10,
borderRadius: 20,
},
});
export default Row;
As you can see each row has a TouchableHighlight when pressed it should navigate to ProfileScreen
EDIT
DO NOT USE NAVIGATOR IT IS DEPRECIATED INSTED USE STACKNAVIGATOR

There is no need to use this.props.Navigator when you are importing the Navigator module. Just use Navigator. this.props contains the list of all the attributes that you pass on to a component
Eg:
Parent.js
class Parent extends React.Component {
render() {
<Child firstProp={"FirstProp"} secondProps={"SecondProp"} />
}
}
Child.js
import {externalImport} from './externalFile'
class Child extends React.Component{
render() {
<div>
I AM FIRST PROP {this.props.firstProp}
I AM SECOND PROP {this.props.secondProp}
I AM NOT A PROP, JUST AN IMPORT { externalImport }
</div>
}
}

Related

Render results on submit react native

New to react native. I want to render what is currently being logged in the console into a separate jsx Text elements, but I've been having trouble.
const handleSubmit = async () => {
const response = await fetch(
`http://127.0.0.1:5000/GetRecipes/${searchText}`,
{
method: "GET",
}
);
const result = await response.json();
const recipes = result["recs"];
for (var meal in recipes) {
if (recipes.hasOwnProperty(meal)) {
console.log(recipes[meal].meal); // want to display this
setSubmitPressed(submitPressed);
}
}
// want to render the results in text elements
const Test = () => {
return <Text>hi</Text>;
};
const DisplayRecipes = ({ recipes }) => {
return (
<View>
<Text>Meals</Text>
{recipes.map((ing) => (
<View key={ing[meal].meal}>
<Text>{ing[meal].meal}</Text>
</View>
))}
</View>
);
};
};
The results should only be displayed after a user submits input from a text input field and the results are fetched.
HandleSubmit is called in the onPress prop of a button.
The reference data looks like this
{0, {“meal”: “Chicken Wings”}}, {1, {“meal”: “Hamburger”}}
Please find below code:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
const recipes = [
{
"id": 0,
"meal": "ChickenWings"
},
{
"id": 1,
"meal": "Hamburger"
}
]
return (
<View style={styles.container}>
<Text>Meals</Text>
{recipes.map((ing) => (
<View key={ing.id}>
<Text>{ing.meal}</Text>
</View>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
You can set your data as per requirement.

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 constructor and componentWillMount scope issue

I want to display all items in my array monthDays with map.
The problem is that i need to execute some logic in a componetWillMount to creact the array by pushing items to it in a loop.
The alterations that i made in the componetWillMount are not afecting the array in the constructor.
Sorry if im not being clear, my english is not that good
Here is the code:
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
export default class Calendar extends Component {
constructor(props){
super(props);
this.state = {text: ''};
this.monthDays = ['I want to put this array'];
}
componentWillMount(){
let monthDays = ['in this scope'];
let defineDays = Number(this.props.days);
let daysCount = 1;
let x = 0;
while (monthDays.length < defineDays) {
monthDays.push(daysCount);
daysCount++;
this.setState({ text : monthDays[x]});
x++;
}
}
render() {
return (
<View style={styles.container}>
{this.monthDays.map(( teste, key ) => (
<View key = { key }>
<Text>{ teste }</Text>
</View>
))}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Ps: defineDays only received a prop with the number of days the month have
You can try this:
componentWillMount() {
let monthDays = ['in this scope'];
let defineDays = Number(this.props.days);
let daysCount = 1;
let x = 0;
while (monthDays.length < defineDays) {
monthDays.push(daysCount);
daysCount++;
this.setState({ text: monthDays[x] });
x++;
}
// Assign the new values to your current array
this.monthDays = monthDays;
}
If you are going to receive props new props over the time you need to do the same thing in componentWillReceiveProps if you want to maintain this.monthDays updated.
Try this:
export default class Calendar extends React.Component{
constructor(){
super();
this.mapFunction = this.mapFunction.bind(this);
}
function mapFunction() {
var arr = []
for(var dayscount=0; dayscount < this.props.days; dayscount++){
arr.push(dayscount);
}
return arr.map((test, key) => {
return (
<View key = { key }>
<Text>{ test }</Text>
</View>
)
})
}
render(){
return(
<View>
{ this.mapFunction }
</View>
)
}
}
Make sure you are setting this.props.days correctly and it's value is an integer.

React Native Flatlist returns the wrong number of empty rows

When I run the code below, it displays 3 empty rows. It should be showing two rows each with a color and enddate and I want to use the 'Parent' as the unique key. The 'Parent' is the unique key created by Firebase when color and enddate were pushed to Firebase with '.push'.
I've tried all sorts of things to get it to display. I did get content to display when I made the 'renderItems' return 'this.state.list', but that returned 3 lines all with the same data, which is the content of the last array on the console log.
I would really appreciate some help to get this working.
Here is the code, a copy of Firebase database and the console.log. Please note that the Firebase 'goal' has been changed to 'color'.
import React, { Component } from 'react';
import { Text, FlatList, View, Image } from 'react-native';
import firebase from 'firebase';
import { Button, Card, CardSection } from '../common';
import styles from '../Styles';
class List extends Component {
static navigationOptions = {
title: 'List',
}
constructor(props) {
super(props);
this.state = {
list: [],
};
}
componentDidMount() {
const { currentUser } = firebase.auth();
const Parent = firebase.database().ref(`/users/${currentUser.uid}/Profile`);
Parent.on(('child_added'), snapshot => {
this.setState({ list: [snapshot.key, snapshot.val().color, snapshot.val().enddate] });
console.log(this.state.list);
});
}
keyExtractor = (item, index) => index;
render() {
return (
<Card>
<View style={{ flex: 1 }}>
<FlatList
data={this.state.list}
keyExtractor={this.keyExtractor}
extraData={this.state}
renderItem={({ item }) => (
<Text style={styles.listStyle}>
{ item.color }
{ item.enddate }
</Text>
)}
/>
</View>
<CardSection>
<Button
style={{
flex: 1,
flexDirection: 'row'
}}
onPress={() => this.props.navigation.navigate('NextPage', { name: 'user' })}
title="Go to next page"
>
Go to next page
</Button>
</CardSection>
</Card>
);
}
}
export { List };
This is the correct way to store the list
componentDidMount() {
const { currentUser } = firebase.auth();
const Parent = firebase.database().ref(`/users/${currentUser.uid}/Profile`);
Parent.on(('child_added'), snapshot => {
const newChild = {
key: snapshot.key,
color: snapshot.val().color,
enddate: snapshot.val().enddate
}
this.setState((prevState) => ({ list: [...prevState.list, newChild] }));
console.log(this.state.list);
});
}
and your keyExtractor
keyExtractor = (item, index) => item.key;

How to select one item of ListView in React-Native?

I am a newbie in React-Native. I want to select one item using ListView. When I first press item, the ListView renderRow() was call, But after all not working!How can I fix this bug? My problem is where?
I wrote a demo in here
I ended up setting empty dataSource initially, then setting it to clone with data variable in componentDidMount. Then, in the onPressRow method, I made the required changes to a copy of data state variable and then set it back to data via setState method. Not sure what your problem was, but this is working now.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
TouchableHighlight
} from 'react-native';
class ListViewDemo extends Component {
constructor(props) {
super(props);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
data: this._genRow(),
dataSource: ds,
}
}
componentDidMount() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.state.data)
});
}
_genRow(){
var datas = [];
for (var i = 0; i < 5; i++) {
datas.push({
row: i,
isSelect: false,
});
}
console.log('datas ' + JSON.stringify(datas));
return datas;
}
render() {
return (
<ListView
dataSource = {this.state.dataSource}
renderRow = {this._renderRow.bind(this)}
renderHeader = {() => <View style={{height: 10, backgroundColor: '#f5f5f5'}} />}
onEndReached = {() => console.log('')}
renderSeparator = {(sectionID, rowID) =>
<View
style={styles.style_separator}
key={`${sectionID} - ${rowID}`}
/>}
/>
);
}
_renderRow(rowData: string, sectionID: number, rowID: number) {
console.log('render row ...');
return (
<TouchableHighlight onPress={this._onPressRow.bind(this.rowID, rowData)}>
<View style={styles.style_row_view}>
<Text style={styles.style_text}>{rowData.row}</Text>
<Text style={styles.style_text}>{rowData.isSelect ? 'true' : 'false'} </Text>
</View>
</TouchableHighlight>
);
}
_onPressRow(rowID, rowData) {
rowData.isSelect = !rowData.isSelect;
var dataClone = this.state.data;
dataClone[rowID] = rowData;
this.setState({
data: dataClone
});
console.log(this.state.data);
}
}
const styles = StyleSheet.create({
style_row_view: {
flex: 1,
flexDirection: 'row',
height: 57,
backgroundColor: '#FFFFFF',
},
style_text: {
flex: 1,
marginLeft: 12,
fontSize: 16,
color: '#333333',
alignSelf: 'center',
},
});
AppRegistry.registerComponent('ListViewDemo', () => ListViewDemo);
try calling the function like this
onPress={(obj1,obj2) => this._onPressRow(rowID, rowData)}

Categories

Resources