ReferenceError: Can't find variable: films in Autocomplete - react-native

I need to use an autocomplete in my app,
I'm using this library because it was the only one I found
https://www.npmjs.com/package/react-native-autocomplete-input
and this way it is working.
import React, { Component } from 'react';
import Autocomplete from 'react-native-autocomplete-input';
export default class Registrar extends Component{
state = {
films: [],
query: '',
}
componentDidMount() {
const json = require('../assets/json/titles.json');
const { results: films } = json;
this.setState({ films });
}
findFilm(query) {
if (query === '') {
return [];
}
const { films } = this.state;
const regex = new RegExp(`${query.trim()}`, 'i');
return films.filter(film => film.title.search(regex) >= 0);
}
render() {
const { query } = this.state;
const films = this.findFilm(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return(
<Autocomplete
autoCapitalize="none"
style={styles.input}
autoCorrect={false}
data={films.length === 1 && comp(query, films[0].title) ? [] : films}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter the film title"
renderItem={({ item }) => (
<TouchableOpacity onPress={() => this.setState({ query: item.title })}>
<Text style={styles.itemText}>
{item.title} ({item.release_date})
</Text>
</TouchableOpacity>
)}
/>
}
}
However, my code requires to be inside a function, as in the example below, but it generates the following error ReferenceError: Can't find variable: films
import React, { Component } from 'react';
import Autocomplete from 'react-native-autocomplete-input';
export default class Registrar extends Component{
state = {
films: [],
query: '',
}
componentDidMount() {
const json = require('../assets/json/titles.json');
const { results: films } = json;
this.setState({ films });
}
findFilm(query) {
if (query === '') {
return [];
}
const { films } = this.state;
const regex = new RegExp(`${query.trim()}`, 'i');
return films.filter(film => film.title.search(regex) >= 0);
}
renderInputField() {
return (
<Autocomplete
autoCapitalize="none"
style={styles.input}
autoCorrect={false}
data={films.length === 1 && comp(query, films[0].title) ? [] : films}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter the film title"
renderItem={({ item }) => (
<TouchableOpacity onPress={() => this.setState({ query: item.title })}>
<Text style={styles.itemText}>
{item.title} ({item.release_date})
</Text>
</TouchableOpacity>
)}
/>
)
}
render() {
const { query } = this.state;
const films = this.findFilm(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return(
{this.renderInputField()}
}
}
I need it to be within a function because this field must appear when answering yes in the previous question
Please, Help me!

Can you try like following. Just add construtor to your class
export default class Registrar extends Component{
construtor(props){
super(props)
this.state = {
films: [],
query: '',
}
}
}
Otherwise check the error line number(it will display with the error), then you can easily find out where its occurred.

Related

how to pass props from flatlist to search?

I have my Main function in one file:
import Search from '../Components/Header';
function Main() {
return (
<View>
<Search />
<FlatList
data={this.state.data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
style={{borderColor: 'black', borderWidth: 1, flexWrap: 'wrap'}}
/>
</View>
And Search class in another file:
const DATA = [
{
id: "1",
title: "Data",
}
];
const Item = ({ title }) => {
return (
<View>
<Text>{title}</Text>
</View>
);
};
const renderItem = ({ item }) => <Item title={item.title} />;
export default class Search extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: DATA,
error: null,
searchValue: "",
};
this.arrayholder = DATA;
};
searchFunction = (text) => {
const updatedData = this.arrayholder.filter((item) => {
const item_data = `${item.title.toUpperCase()})`;
const text_data = text.toUpperCase();
return item_data.indexOf(text_data) > -1;
});
this.setState({ data: updatedData, searchValue: text });
};
render() {
return (
<View style={Headerstyles.rectangle}>
<SearchBar
value={this.state.searchValue}
onChangeText={(text) => this.searchFunction(text)}
/>
</View>
);
}
}
So as I understand I should pass props from Flatlist to Search class, but I get an error TypeError: Cannot read property 'data' of undefined. I think it's not only about data and also renderItem and keyExtractor.
How can I do this?
The component Main does not contain a state called data. This state is defined in Search. I would create the state inside Main and pass the setter function to Search.
function Main() {
const [data, setData] = React.useState(DATA);
return (
<View>
<Search setData={setData} />
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
style={{borderColor: 'black', borderWidth: 1, flexWrap: 'wrap'}}
/>
</View>
)
}
Then, use the new props in Search as follows.
export default class Search extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
error: null,
searchValue: "",
};
this.arrayholder = DATA;
};
searchFunction = (text) => {
const updatedData = this.arrayholder.filter((item) => {
const item_data = `${item.title.toUpperCase()})`;
const text_data = text.toUpperCase();
return item_data.indexOf(text_data) > -1;
});
this.setState({ searchValue: text });
this.props.setData(updatedData);
};
render() {
return (
<View style={Headerstyles.rectangle}>
<SearchBar
value={this.state.searchValue}
onChangeText={(text) => this.searchFunction(text)}
/>
</View>
);
}
}

Why react-native doesn't render all content when 2 render methods included in it?

I have some content to be rendered conditionally and some fixed content i.e. footer. I dont want to render footer every time when state changes, hence I've added two methods renderContent() and renderFooter to be called in render() method.
Below code, doesn't render both methods.
'use strict';
import React, { Component } from 'react';
import { Alert, FlatList, View, StyleSheet, Text, Linking, Button } from 'react-native';
import { AsyncStorage } from 'react-native';
import getEnvVars from '../environment';
const { apiUrl } = getEnvVars();
import Moment from 'moment';
import { Ionicons } from '#expo/vector-icons';
import FootBar from '../screens/FootBar';
import { LinesLoader } from 'react-native-indicator';
export default class SubscriptionsToEnd extends Component {
static navigationOptions = ({ navigation }) => {
const { state } = navigation;
return {
title: `${state.params && state.params.title ? state.params.title : 'Subscriptions Due'}`,
};
};
constructor(props) {
super(props);
this.state = {
isLoaded: false,
dataSource: [],
title: 'Subscriptions Due'
};
}
componentDidMount() {
this._getAllCustomers();
}
_getAllCustomers() {
let url;
if (this.state.title === 'Subscriptions Due') {
url = apiUrl + "/customersWithSubscriptionNearToEnd/";
this.props.navigation.setParams({ title: 'Subscriptions Due' })
}
if (this.state.title === 'Customers') {
url = apiUrl + "/customers/";
this.props.navigation.setParams({ title: 'Customers' })
}
this.setState({ isLoaded: false })
try {
AsyncStorage.multiGet(['role', 'jwt']).then((data) => {
let role = data[0][1];
let jwt = data[1][1];
if (role === 'Admin') {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'jwt': jwt
},
}).then(res => res.json())
.then(
(result) => {
if (result.message != 'Unauthorized user!' && this.state.title === 'Customers') {
this.setState({
isLoaded: true,
dataSource: result,
title: 'Subscriptions Due'
});
} else if (result.message != 'Unauthorized user!' && this.state.title === 'Subscriptions Due') {
this.setState({
isLoaded: true,
dataSource: result,
title: 'Customers'
});
} else if (result.message === 'Unauthorized user!') {
this.props.navigation.navigate('Login');
}
},
(error) => {
console.log(error);
this.setState({
isLoaded: true
});
this.props.navigation.navigate('Login');
}
)
}
})
} catch (error) {
console.log('Error at getting token \n' + error)
}
}
GetGridViewItem(id) {
Alert.alert(id);
}
_logOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
_addCustomer() {
// TBD
}
renderContent() {
if (!this.state.isLoaded) {
return (
<View style={styles.loader}>
<LinesLoader color='#1d91a5' barWidth={5} barHeight={60} barNumber={5} betweenSpace={5} />
</View>
)
}
if (this.state.isLoaded) {
return (
<View style={styles.container}>
<View style={styles.grid}>
<FlatList
data={this.state.dataSource}
renderItem={({ item }) =>
<View style={styles.GridViewContainer}>
<Text style={styles.GridViewTextLayout}>
<Text onPress={this.GetGridViewItem.bind(this, item._id)}>
<Text style={styles.Name}>{item.firstname}</Text> <Text style={styles.Name}>{item.lastname}</Text> {"\n"}
<Text>{Moment(item.till_date).format('Do MMM YYYY')} </Text>{"\n\n"}
</Text>
<Text onPress={() => { Linking.openURL('tel:+44' + item.mobile); }}><Ionicons name="md-phone-portrait" size={22} color="#1d91a5" /> {item.mobile}</Text> {"\n\n"}
<Text><Ionicons name="md-mail" size={22} color="#1d91a5" />{item.email}</Text>
</Text>
</View>}
numColumns={2}
keyExtractor={(item, index) => index.toString()}
/>
</View >
</View>
)
};
}
renderFooter() {
return (
<View style={styles.buttonsContainer}>
<View style={styles.button}>
<Button color='#1d91a5' title={this.state.title} onPress={this._getAllCustomers.bind(this)} />
</View>
<View style={styles.button}>
<Button color='#1d91a5' title="+Customer" onPress={this._addCustomer.bind(this)} />
</View>
<View style={styles.button}>
<Button color='#1d91a5' title="Logout" onPress={this._logOutAsync.bind(this)} />
</View>
</View>
);
}
render() {
return (
this.renderContent(),
this.renderFooter()
);
}
}
Above code only renders this.renderFooter() method. If I swap methods in render(), it renders this.renderContent().
Can someone please tell me why it is failing to render both?
I was doing it wrong. Main render() method should be like:
render() {
return (
<View style={styles.wrapper}>
{this.renderContent()}
{this.renderFooter()}
</View>
);
}
It looks like you figured it out just before I could post my answer.
The return function can only return one view. Your 2 functions each return a view. So wrapping both functions in a single view solves the problem.

Passing data from one component to another in React Native

I am setting Sub Domain URL's for single app. Sub domain name will enter at the first time. it saves to the async storage and need to retrieve it from a common component
Using the const, it's not working properly.
Here is the partially completed code. baseURL and socketURL is needed inside another component function. How can I access these constants from there ?
index_new.js
import * as React from 'react';
import { View } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
import Login from "../screens/common/login/login/login";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
sub_domain: '',
};
}
async getSchoolCode() {
let sub_domain = '';
try {
sub_domain = await AsyncStorage.getItem('SCHOOL_CODE') || ''
} catch (error) {
}
return sub_domain;
};
async setSubdomain() {
const sub_domain = await this.getschoolcode()
await this.setState({ sub_domain })
}
getBaseUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:81/`;
}
getSocketIoUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:8080/`;
}
async componentDidMount() {
await this.setSubdomain();
}
render() {
const baseUrl = this.getBaseUrl();
const socketIoUrl = this.getSocketIoUrl();
const extraProps = {
baseUrl,
socketIoUrl
}
return (
<View>
<Login {...extraProps} />
</View>
)
}
}
Login.js
import React, { Component } from 'react'
import {
Alert,
Keyboard,
Text,
View,
TextInput,
TouchableHighlight,
Image,
ActivityIndicator,
StatusBar,
} from 'react-native'
import config from "../../../../config";
import styles from './style'
import { Icon } from "react-native-elements";
import Toaster from '../../../../components/toaster'
import AsyncStorage from '#react-native-community/async-storage';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
credentials: {
schoolcode: "",
email: "",
password: "",
},
loading: false,
school_code: '',
};
}
async getschoolcode() {
let school_code = '';
try {
school_code = await AsyncStorage.getItem('SCHOOL_CODE') || ''
} catch (error) {
}
return school_code;
};
updateText(text, field) {
let newCredentials = Object.assign(this.state.credentials);
newCredentials[field] = text;
// setState should be done like this
this.setState({
credentials: newCredentials
})
if(field == 'schoolcode'){
AsyncStorage.setItem('SCHOOL_CODE', text);
this.getschoolcode().then((keyValue) => {
this.state.school_code = keyValue;
console.log(this.state.school_code);
});
}
}
async login() {
Keyboard.dismiss();
let credentials = this.state.credentials;
if (this.state.credentials.schoolcode == '' || this.state.credentials.email == '' || this.state.credentials.password == '') {
Toaster.toast('Please Enter a valid UserName and Password', '#d30000')
} else {
const that = this;
credentials.email = that.state.credentials.email;
this.setState({ loading: !this.state.loading });
const new_url = this.props.baseUrl;
fetch(config.baseURL + 'mobileapi/get_token/?username=' + `${that.state.credentials.email}` + '&password=' + `${that.state.credentials.password}`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
credentials: credentials,
}),
})
.then((response) => response.json())
.then(responseJson => {
if (responseJson.confirmation === "success") {
AsyncStorage.setItem('USER_ID', responseJson.data.user_id.toString());
this.setState({ loading: !this.state.loading });
setTimeout(() => {
this.props.navigation.navigate("Home")
}, 500);
} else {
this.setState({ loading: !this.state.loading });
setTimeout(() => {
Toaster.toast('Please Enter a valid UserName and Password', '#d30000')
// throw new Error(responseJson.message);
}, 500);
}
})
.catch((err) => {
//stop loading
this.setState({ loading: !this.state.loading });
setTimeout(() => {
if (JSON.stringify(err.message) === JSON.stringify('Network request failed')) {
Toaster.toast('Please check your internet connection or try again later', '#d30000')
}
}, 500);
})
}
}
render() {
const loginText = (this.state.loading) ? 'Loading' : 'Login';
return (
<View style={styles.container}>
<StatusBar backgroundColor="#2383c9"
translucent={true}
hidden={false}/>
<Image source={require('../../../../assets/images/icons/logo.png')}
style={{ width: 99, height: 99, margin: 5, }} />
<Text style={{ fontSize: 20, margin: 20, color: "#ffffff" }}>Vidhyadhan</Text>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/username.png')} />
<TextInput style={styles.inputs}
placeholder="School-Code"
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'schoolcode')
}} value={this.state.schoolcode}
autoCorrect={false}
autoCapitalize={"none"}
/>
</View>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/username.png')} />
<TextInput style={styles.inputs}
placeholder="Username"
keyboardType="email-address"
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'email')
}} value={this.state.email}
autoCorrect={false}
autoCapitalize={"none"}
/>
</View>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/password.png')} />
<TextInput style={styles.inputs}
placeholder="Password"
secureTextEntry={true}
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'password')
}}
value={this.state.password}
autoCorrect={false}
secureTextEntry />
</View>
<TouchableHighlight style={[styles.buttonContainer, styles.loginButton]}
onPress={this.login.bind(this)} >
<View style={{ justifyContent: 'center', flex: 1, flexDirection: 'row' }}>
{this.state.loading === false ?
<Icon name='login' type='entypo' size={16} color='white' /> :
<ActivityIndicator size="small" color="#ffffff" />}
<Text style={styles.loginText}> {loginText} </Text>
</View>
</TouchableHighlight>
</View>
);
}
}
export default Login;
First, You're not setting the state correctly. Then, you're trying to set the state twice.
Here's a better way of doing it:
import * as React from 'react';
import { View } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
sub_domain: '',
};
}
async getSchoolCode() {
let sub_domain = '';
try {
sub_domain = await AsyncStorage.getItem('sub_domain') || ''
} catch (error) {
}
return sub_domain;
};
async setSubdomain() {
const sub_domain = await this.getschoolcode()
await this.setState({ sub_domain })
}
getBaseUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:81/`;
}
getSocketIoUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:8080/`;
}
async componentDidMount() {
await this.setSubdomain();
}
render() {
const baseUrl = this.getBaseUrl();
const socketIoUrl = this.getSocketIoUrl();
return (
<View/>
);
}
}
Based on the comments, here's how your render can be:
render() {
const baseUrl = this.getBaseUrl();
const socketIoUrl = this.getSocketIoUrl();
const extraProps = {
baseUrl,
socketIoUrl
}
return (
<View>
<MyFirstComponent {...extraProps} />
<MySecondComponent {...extraProps} />
</View>
)
}
And in your MyFirstComponent, you can either use this.props.baseUrl or this.props.socketIoUrl

React Native Search using SectionList

I created a SectionList and tried to implement a search filter for my SectionList. But my output got an error. I took a screenshot of it below. I don't know what's wrong.
This is my component.
export default class Cluster1 extends Component{
constructor(props){
super(props)
this.state = {
dataToShow: '',
search: false
}
}
searchUpdated = (term) => {
let matchedItemsArray = []
if(term === ''){
this.setState({search: false, dataToShow: ClusterData})
}else{
this.state.dataToShow.map((item) => {
if(item.title.includes(term)){
matchedItemsArray.push(item)
}
})
this.setState({search: true, dataToShow: matchedItemsArray})
}
}
searchUpdated = (input) => {
let userInput =[]
if(input === ''){
this.setState({search: false})
userInput = ''
}else{
this.setState({search: true})
}
}
render(){
return(
<View style={styles.container}>
<TextInput
onChangeText={(term) => { this.searchUpdated(text) }}
style={styles.searchInput}
placeholder="Type a mood to search"
/>
<SectionList
renderItem = {({item, index}) =>
<SectionListItem item = {item} index = {index}/>}
renderSectionHeader = {({section}) =>
<SectionHeader
sections={this.searchUpdated()}
keyExtractor = {(item) => item.name}/>}>
</SectionList> </View>
);
}}
class SectionHeader extends Component {
render() {
return (
<View style={styles.header}>
<Text style={styles.headertext}>
{this.props.section.title}
</Text>
<TouchableOpacity onPress={ () => Actions.SongList({ section: this.props.section}) }>
<Text style ={styles.Play}> Play
</Text>
</TouchableOpacity>
</View>
); }
}
class SectionListItem extends Component{
render(){
return(
<View>
<Text style={styles.moodname}>{this.props.item.name}</Text>
</View>
);
}}
This is my data
const ClusterData = [
{ title: 'Cluster1',
data:
[
{name: 'passionate'},{name: 'rousing'},{name: 'confident'},
{name: 'boisterous'},{name: 'rowdy'}],
},
{
title: 'Cluster2',
data:
[
{name: 'rollicking'},{name: 'cheerful'{name: 'fun'},{name: 'sweet'},
{name: 'amiable'},{name: 'natured'}],
Here is a simple search filter:
I added a search state to help determine whether the user is currently searching or not.
constructor(props){
super(props)
this.state = {
dataToShow: '',
search: false
}
}
Then, we create the search function.
searchUpdated = (term) => {
let matchedItemsArray = []
if(term === ''){
this.setState({search: false, dataToShow: ClusterData})
}else{
this.state.dataToShow.map((item) => {
if(item.title.includes(term)){
matchedItemsArray.push(item)
}
})
this.setState({search: true, dataToShow: matchedItemsArray})
}
}
When the input is '', the search state is false. Otherwise, the function will map through the dataToShow array to find if any section titles include the user's input.
Alternatively, I like to use a lodash filter instead for it's simplicity.
First, we declare a constant called userInput:
let userInput
Then, we create a function to determine whether the userInput is empty or not to set the search state. (Remember to keep this.state.search that we created in the first place)
searchUpdated = (input) => {
if(input === ''){
this.setState({search: false})
userInput = ''
}else{
this.setState({search: true})
}
}
Finally, in our SectionList we use the lodash filter to help filter for the right section header names:
<SectionList
renderItem = {({item, index}) =>
<SectionListItem item = {item} index = {index}/>}
renderSectionHeader = {({section}) =>
<SectionHeader
section = {section}
sections = {
this.state.search ?
_.filter(this.state.dataToShow, function(item){
return item.title.includes(userInput)})
: this.state.dataToShow}
keyExtractor = {(item) => item.name}/>}>
</SectionList>
The entire component
import React from 'react'
import { View, Text, SectionList, TouchableOpacity, TextInput } from 'react-native'
const ClusterData = [
{title: 'Cluster1', data: [{name: 'passionate'},{name: 'rousing'},{name: 'confident'},{name: 'boisterous'},{name: 'rowdy'}]},
{title: 'Cluster2', data: [{name: 'rollicking'},{name: 'cheerful'},{name: 'fun'},{name: 'sweet'},{name: 'amiable'},{name: 'natured'}]}
]
let userInput = ''
export default class TempScreen extends React.Component {
constructor(props){
super(props)
this.state = {
search: false,
dataToShow: []
}
}
componentWillMount(){
this.setState({dataToShow: ClusterData})
}
searchUpdated = (term) => {
let matchedItemsArray = []
if(term === ''){
this.setState({search: false, dataToShow: ClusterData})
}else{
this.setState({search:true, dataToShow: ClusterData}, function(){
this.state.dataToShow.map((item) => {
if(item.title.includes(term)){
matchedItemsArray.push(item)
}
})
this.setState({dataToShow:matchedItemsArray})
})
}
}
render () {
return (
<View>
<TextInput
onChangeText={(term) => {this.searchUpdated(term)}}
style={styles.searchInput}
placeholder="Type a mood to search"/>
<SectionList
renderItem={({item}) => <SectionListItem itemName = {item.name}/>}
renderSectionHeader={({section}) => <SectionHeader sectionTitle = {section.title}/>}
sections={this.state.dataToShow}
/>
</View>
)
}
}
class SectionHeader extends React.Component{
render(){
return(
<View>
<Text>{this.props.sectionTitle}</Text>
<TouchableOpacity>
<Text>Play</Text>
</TouchableOpacity>
</View>
)
}
}
class SectionListItem extends React.Component{
render(){
return(
<View>
<Text>{this.props.itemName}</Text>
</View>
)
}
}

How to Select Item in a ListView with Section Headers React Native

Am trying to implemented a grouped contacts with Section headers set to Alphabet letters. With a simple list(Without section headers) am able to select a contact but i can't seem to make it work with a section header.
class ContactsView extends React.Component{
constructor(props) {
super(props);
this.checkContact = this.checkContact.bind(this);
this.renderSectionHeader = this.renderSectionHeader.bind(this);
this.toArray = this.toArray.bind(this);
let contacts = this.props.contacts || [];
let ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
});
this.state = {
contacts:contacts,
dataSource:ds
}
}
componentDidMount() {
ToastAndroid.show('Component will did mount', ToastAndroid.SHORT);
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.state.contacts)
});
}
componentWillMount(){
//Clear the selected invitees.
ToastAndroid.show('Component contacts did mount', ToastAndroid.SHORT);
}
componentWillUpdate(){
//Check if there are selected contacts and update invitees.
//this.invitees = this.getInvitees();
ToastAndroid.show('Component will update', ToastAndroid.SHORT);
}
onLoadMore(){
return;
}
renderContact(item,sectionID, rowID){
let icon = <Icon name="check-box-outline-blank" size={25} style={styles.checkIcon} color="#83D8F8" />;
if(item.checked)
icon = <Icon name="check-box" size={25} style={styles.checkIcon} color="#4ABF30" />
let avatar = <Image styleName="small-avatar" style={styles.avatar} source={{uri: item.thumbnailPath}} />
if(!item.thumbnailPath){
avatar = <MaterialInitials style={styles.avatar} backgroundColor={item.color} color={'white'} size={40} text={item.fullName} single={false} />
}
if(rowID === 0){
styles.caption['borderTopColor'] = '#fff';
}
//let itemIndex =
console.log(item.checked)
return (
<TouchableHighlight onPress={() => this.checkContact(item,sectionID,rowID)}>
<Row style={styles.itemRow}>
{icon}
{avatar}
<View styleName="vertical" style={styles.caption}>
<Subtitle styleName="bold multiline" style={styles.itemTitle}>{item.fullName}</Subtitle>
<Caption style={styles.itemPhone}>{item.phone}</Caption>
</View>
</Row>
</TouchableHighlight>
)
}
renderSectionHeader(sectionData, sectionID){
return (
<View key={sectionID} style={{marginTop:0,marginBottom:0,paddingBottom:0,paddingTop:0}}>
<Text style={{fontFamily:'Cabin_Bold',fontSize:20,color:'#1d313c',marginLeft:11,marginBottom:10,marginTop:10}}>
{sectionID}
</Text>
</View>
)
}
checkContact(item,sectionID,id){
let dataClone = Object.assign({},this.state.contacts);
dataClone[sectionID][id] = { ...dataClone[sectionID][id], checked: !item.checked }
this.setState({
contacts: dataClone,
dataSource:this.state.dataSource.cloneWithRowsAndSections(dataClone)
});
console.log(this.state.contacts);
//this.props.dispatch(ContactsState.check(sectionID,id,item));
}
toArray(_Object){
let _Array = new Array();
for(let name in _Object){
_Array[name] = _Object[name];
}
return _Array;
}
render() {
return (
<ListView
style={{marginBottom:0,marginTop:0,paddingBottom:0,paddingTop:0}}
dataSource ={this.state.dataSource}
renderSectionHeader={this.renderSectionHeader}
initialListSize={1}
pageSize={10}
scrollRenderAheadDistance ={360}
renderRow={this.renderContact.bind(this)}
/>
);
}
}
export default ContactsView;
I should be able to click on the item and have it's check box ticked.
A simple example :
import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View, TouchableOpacity } from 'react-native';
class ListViewBasics extends Component {
// Initialize the hardcoded data
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
{
id: 1,
title: 'title1'
},
{
id: 2,
title: 'title2'
},
{
id: 3,
title: 'title3'
},
]),
checkedItems: {}
};
}
checkMark(rowId){
let marked = this.state.checkedItems;
if(market.hasOwnProperty(rowId)){
if(marked[rowId]){
marked[rowId] = false;
} else {
marked[rowId] = true;
}
} else {
marked[rowId] = true;
}
this.setState({checkedItems: marked})
}
renderCheckMark(rowId){
let marked = this.state.checkedItems;
if(marked.hasOwnProperty(rowId)){
if(market[rowid]){
return true;
} else {
return false;
}
} else {
return false;
}
}
renderItem(rowData){
<TouchableOpacity
onPress={()=>{
this.checkMark(rowData.id)
}}
>
<Text>{rowData.title} {this.renderCheckMark(rowData.id) ? 'X' : null}</Title>
</TouchableOpacity>
}
render() {
return (
<View style={{flex: 1, paddingTop: 22}}>
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => { this.renderItem(rowData) }}
/>
</View>
);
}
}
// App registration and rendering
AppRegistry.registerComponent('ListViewBasics', () => ListViewBasics);
If the item is selected, an 'X' will be shown after row title.