In React-native, how to handle checkbox in Listview? - react-native

In my react-native app, I am trying to show my contact details with checkboxes for selecting.
Here is my code:
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData, sectionID, rowID) => (
<TouchableHighlight onPress={() => this.goRideDetails(rowData)}>
<Text style={styles.rideHeader}>{rowData.name} </Text>
<CheckBox
checked={this.state.checked}
onCheckBoxPressed={() =>
this.setState({ checked: !this.state.checked })
}
/>
</TouchableHighlight>
)}
/>
In my view checkbox is displaying on every row, but not working.
Any one can help me. Thanks in advance.

You can easily do this with component separation. Please, take a look here:
export default class ContactList extends Component {
static propTypes = {
contacts: React.PropTypes.array,
}
static defaultProps = {
contacts: [],
}
constructor(){
super();
this._renderRow = this._renderRow.bind(this);
}
_renderRow(rowData,sectionID,rowID) {
return <Contact info={ rowData } />;
}
render() {
return (
<ListView
dataSource={ this.props.contacts }
renderRow={ this._renderRow }
/>
);
}
}
export class ContactList extends Component {
static propTypes = {
info: React.PropTypes.object.isRequired,
}
constructor(){
super();
this.goRideDetails = this.goRideDetails.bind(this);
this.setChecked = this.setChecked.bind(this);
}
goRideDetails() {
//your logic here
}
setChecked() {
this.props.info.checked = !this.props.info.checked; //will be much better to do it with redux and action creators
}
render() {
return (
<TouchableHighlight onPress={ this.goRideDetails }>
<Text style={ styles.rideHeader }>{this.props.info.name} </Text>
<CheckBox checked={ this.props.info.checked } onCheckBoxPressed={ this.setChecked } />
</TouchableHighlight>
);
}
}
After that you can simply call:
<ContactList contacts={this.state.dataSource} />
in your jsx and voila.
Important note: Do not use array functions inside your jsx code blocks.
Important note 2: Try to start using redux or flux for storing state of your application. It will be provide much better code design.
Hope, it will help.

import React , {Component} from 'react'
import {
Text,
View,
ListView,
StyleSheet,
TouchableOpacity,
Image,
} from 'react-native'
import CheckBox from 'react-native-checkbox'
var Folder = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
var folder = '' ////// all the new folder
var check_folder = [] ////// all the check box conditions
class ApproveContent extends Component {
///////// all the upper thing that are global variable for this script is has same value as that of the state the only reason we are using this because of the layout update //////////
state={
folder:[],
data:[],
check:[]/////// this need to do just to upadte the layout of the check box
}
render(){
return(
<View style = {{flex:1,backgroundColor:'white',alignItems:'center'}}>
<ListView
dataSource={Folder.cloneWithRows(this.state.folder)}
renderRow={(rowData,rowID,sectionID) => <View style = {{ alignItems: 'center',margin:5}}>
<TouchableOpacity style = {{width:Dimension.ScreenWidth/1.2,height:Dimension.ScreenHeight/6,flexDirection: 'row',alignItems:'center'}}
onPress={() => {}}>
<CheckBox
label=''
labelBefore={false}
checked={this.state.check[sectionID]}
checkboxStyle = {{marginLeft: 20}}
onChange={(checked) => {
this.setState({
check:!this.state.check
})
if(check_folder[sectionID] == false){
check_folder[sectionID] = true
this.setState({
check:check_folder// has to do this because we cant change the single element in the array
})
}else{
check_folder[sectionID] = false
this.setState({
check:check_folder// has to do this because we cant change the single element in the array
})
}
console.log(check_folder)a
}}
/>
</TouchableOpacity>
</View>
}
/>
</View>
)}
}
export default ApproveContent
const style = StyleSheet.create({
TextStyle:{
fontFamily: 'Roboto-Bold',
fontSize:15,
},
approveButton: {
bottom:0,
left:0,
alignItems: 'center',
}
})

Related

React native components not working properly

I have this custom button component :
import React, { Component } from "react";
import { View, Text, TouchableOpacity } from "react-native";
import { styles } from "./styles";
class TransactionFees extends Component {
test = () => {
const { speed, eth, usd,pressed,changeColor } = this.props;
console.log("speed, eth, usd,pressed,changeColor",speed, eth, usd,pressed,changeColor)
}
render() {
const { speed, eth, usd,pressed,changeColor,key } = this.props;
return (
<TouchableOpacity style={ pressed ? styles.pressedButton : null } onPress={changeColor}>
<Text style={styles.transactionFeeTitle}>{speed}</Text>
<Text style={styles.transactionFeeSubText}>{eth} ETH</Text>
<Text style={styles.transactionFeeSubText}>$ {usd} </Text>
</TouchableOpacity>
);
}
}
export default TransactionFees;
This is how I use it:
<TransactionFees pressed={pressed} changeColor={this.changeColor} key={"slow"} speed={"Slow"} eth={"0.000010"} usd={"0.02"} />
<TransactionFees pressed={pressed} changeColor={this.changeColor} key={"average"} speed={"Average"} eth={"0.000030"} usd={"0.03"} />
<TransactionFees pressed={pressed} changeColor={this.changeColor} key={"fast"} speed={"Fast"} eth={"0.000050"} usd={"0.04"} />
changeColor function:
changeColor = () => {
const {pressed} = this.state
this.setState({pressed:!pressed})
}
When clicked I want only one of them to change the background style but the problem is when clicking on any of them,all of them change the background stye any solutions on how to solve this please?
You will have to maintain the pressedKey in the parent instead of maintaining the pressed something like below. Here we pass the selected key using the changeColor and maintain that in the parent which will be used to compare and decide the backgroud color.
class TransactionFeesContainer extends Component {
state = {
selectedKey: null,
};
changeColor = (key) => {
this.setState({ selectedKey: key });
};
render() {
return (
<View>
<TransactionFees
selectedKey={this.state.selectedKey}
changeColor={this.changeColor}
speedkey={'slow'}
speed={'Slow'}
eth={'0.000010'}
usd={'0.02'}
/>
<TransactionFees
selectedKey={this.state.selectedKey}
changeColor={this.changeColor}
speedkey={'average'}
speed={'Average'}
eth={'0.000030'}
usd={'0.03'}
/>
<TransactionFees
selectedKey={this.state.selectedKey}
changeColor={this.changeColor}
speedkey={'fast'}
speed={'Fast'}
eth={'0.000050'}
usd={'0.04'}
/>
</View>
);
}
}
class TransactionFees extends Component {
render() {
const { speed, eth, usd, speedkey, selectedKey } = this.props;
return (
<View>
<TouchableOpacity
style={speedkey === selectedKey ? styles.pressedButton : null}
onPress={() => {
this.props.changeColor(speedkey);
}}>
<Text style={styles.transactionFeeTitle}>{speed}</Text>
<Text style={styles.transactionFeeSubText}>{eth} ETH</Text>
<Text style={styles.transactionFeeSubText}>$ {usd} </Text>
</TouchableOpacity>
</View>
);
}
}

I built my own button component and I can't get it to work

I built my own button component using React Native and I am trying to use it in my code. All I am trying to do for now is to get it to log a console message and for some reason it's not doing it.
Button Component
import React, {Component} from 'react';
import { Text, TouchableOpacity } from 'react-native';
class Button extends Component {
render(){
const { onPress, children } = this.props;
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={ onPress } style={ buttonStyle }>
<Text style={ textStyle }>
{ children }
</Text>
</TouchableOpacity>
);
}
}
Code using the Button
class FlashcardMenuDetail extends Component {
onButtonPress() {
console.log('You pushed the button');
}
render() {
const { title } = this.props.flashcard;
return (
<Card>
<CardItem>
<Button onpress={this.onButtonPress.bind(this)}>
{ title }
</Button>
</CardItem>
</Card>
);
}
}
I can push the button but nothing shows up on the console window.
There is no arrow function in the button you made onPress When the function of the function is used, only the console log is left because the return value is imported. You have to add an arrow function.
export const makeButton = (props) => {
const { title = "title", buttonStyle = {}, textStyle = {}, onPress } = props;
return (
<TouchableOpacity onPress={() => onPress} style={buttonStyle}>
<Text style={textStyle}>{props.title}</Text>
</TouchableOpacity>
);
};
Usage
import { makeButton } from 'path/makeButton.js';
<Card>
<CardItem>
<makeButton
title="get console.log"
onPress={() => onButtonPress() }
buttonStyle ={{ /* some styles for button */ }}
textStyle={{ /* styles for button title */ }}
/>
</CardItem>
</Card>

Is there a way to make an onpress navigation inside a result of a array map in react-native?

I have a simple array of names in this.state.members. I used the map function to iterate the members.
I want to add a button for each member so, when i click to them, i go to a screen dedicated to each member.
I test the button outise the loop, it is working.
But for those inside the map function, its says "undefined is not an object (evaluating '_this2.props.navigation')
onPress"
i spent a whole day trying to understand what is going on without any luck.
Any help will be appreciated.
Here is my code
import { StyleSheet, Text, View } from "react-native";
import { ListItem, Button } from "react-native-elements";
export default class ProfileScreen extends React.Component {
static navigationOptions = {
header: null
};
constructor(props) {
super(props);
this.state = {
members: ["Allie", "Gator", "Lizzie"],
id_groupe: "",
groupe: ""
};
// this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
const members = this.state.members;
const namesList = members.map(function(name, i) {
return (
<Button
onPress={() => this.props.navigation.navigate("Home")}
title={name}
key={i}
/>
);
});
return (
<View style={styles.container}>
<View>{namesList}</View>
<View>
<Button
title="Back to home"
onPress={() => this.props.navigation.navigate("Home")}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
just use an arrow function, and you will not have the this issue. But not sure about your actual problem, if the arrow function works let's check if you have any other issue.
Arrow function when you iterate with map:
const namesList = members.map((name, i) => {
return (
<Button
onPress={() => this.props.navigation.navigate("Home")}
title={name}
key={i}
/>
);
});

Expo SDK 29 FlatList onRefresh not calling

Using Expo SDK 29 for a react native application.
I would like to use a flat list component. This makes up the entirety of a SafeAreaView component. I make this point as there are lots of issues relating to a flat list inside of a scroll view which this is not.
The flat list shows a list of jobs.
I have added a jobLoading boolean to the redux state to manage when the list should show as refreshing and can confirm that this toggles as expected when firing the actions to fetch the data and the success.
When i add the props to the flat list for onRefresh and refreshing the component seems to work by showing the activity indicator in the UI but does not fire the onRefresh function. I have tried implementing the call in numerous ways but nothing happens. The result is that the activity indicator shows itself and never disappears.
As it's Expo SDK 29 the React Native version is 0.55.4
Anyone have any ideas of what to try. I've spent a couple of hours looking at this trying various things but suggestions are welcome.
Thanks in advance.
EDIT: Added the code for reference. Reducer for refreshing sets true when fetchJobs() is dispatched and false when a success or error is recieved. The console log for onRefresh never triggers.
import * as React from 'react'
import * as actions from '../../redux/actions'
import { ActivityIndicator, FlatList, KeyboardAvoidingView, Dimensions, SafeAreaView, StyleSheet, View } from 'react-native'
import { ApplicationState, JobState, Job } from '../../redux'
import { Button, Form, Input, Item, Text, Icon } from 'native-base'
import { JobListItem } from './jobListItem'
import { StateHandlerMap, compose, lifecycle, withPropsOnChange, withStateHandlers } from 'recompose'
import { connect } from 'react-redux'
interface ReduxStateProps {
jobs: JobState
refreshing: boolean
screenOrientation: string
}
interface ReduxDispatchProps {
fetchJobs: (param?: string) => any
}
export interface DataItem {
key: string
data: Job
}
interface ListProps {
jobList: DataItem[]
}
interface SearchStateProps {
timer: number | undefined
searchString: string
}
interface SearchHandlerProps extends StateHandlerMap<SearchStateProps> {
updateSearch: (searchString: string) => any
setTimer: (timer: number | undefined) => any
}
type OuterProps = {}
type InnerProps = OuterProps & ReduxStateProps & ReduxDispatchProps & ListProps & SearchStateProps & SearchHandlerProps
const enhance = compose<InnerProps, OuterProps>(
connect<ReduxStateProps, ReduxDispatchProps, OuterProps, ApplicationState>(
state => ({
jobs: state.job,
refreshing: state.jobLoading,
screenOrientation: state.screenOrientation
}),
dispatch => ({
fetchJobs: (param?: string) => dispatch(actions.jobs.request({ param }))
})
),
withPropsOnChange<ListProps, OuterProps & ReduxStateProps & ReduxDispatchProps>(
['jobs', 'screenOrientation'],
props => ({
jobList: props.jobs && Object.keys(props.jobs).map(job => ({ key: job, data: props.jobs[Number(job)] }))
})
),
withStateHandlers<SearchStateProps, SearchHandlerProps, OuterProps>(
{
timer: undefined,
searchString: ''
},
{
updateSearch: state => (searchString: string) => ({ searchString }),
setTimer: state => (timer: number | undefined) => ({ timer })
}
),
lifecycle<InnerProps, {}>({
componentDidMount() {
this.props.fetchJobs()
}
})
)
export const JobList = enhance(({ fetchJobs, jobList, refreshing, screenOrientation, searchString, setTimer, timer, updateSearch }) => {
const onSearchChange = (search: string) => {
clearTimeout(timer)
updateSearch(search)
const timing = setTimeout(() => {
fetchJobs(search)
}, 500)
setTimer(timing)
}
const onRefresh = () => {
console.log('requesting refresh')
fetchJobs()
}
return (
<SafeAreaView style={{ flex: 1}}>
<KeyboardAvoidingView style={{ flexDirection: 'row', justifyContent: 'space-evenly', paddingTop: 3, paddingRight: 3 }}>
<Form style={{ flex: 1, paddingLeft: 10, paddingRight: 10 }}>
<Item>
<Input
value={searchString}
onChangeText={(text: string) => onSearchChange(text)}
placeholder='Search'
/>
</Item>
</Form>
<Button onPress={() => {fetchJobs(); updateSearch('')}}>
<Icon name='refresh' />
</Button>
</KeyboardAvoidingView>
{refreshing &&
<View style={styles.refreshContainer}>
<Text style={{ paddingBottom: 10 }}>Fetching Data</Text>
<ActivityIndicator />
</View>
}
<FlatList
keyExtractor={item => item.key}
data={jobList}
renderItem={({ item }) =>
<JobListItem
screenOrientation={screenOrientation}
item={item}
/>
}
onRefresh={onRefresh}
refreshing={refreshing}
/>
</SafeAreaView>
)
})
const styles = StyleSheet.create({
refreshContainer: {
height: 60,
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
})
I'm having the exact same issue and I'm using expo SDK 30. But my case is a little bit different. The onRefresh function is called everytime I pull, however if I scroll down my list, and scroll back up fast, the loading indicator shows up, but my onRefresh function is not called.
My refreshing prop is set on my reducer, and my onRefresh function dispatches an action that fetches data and set refreshing true and false.
Here is my code:
class NoticiasScreen extends Component {
static navigationOptions = {
header: <Header
title='Notícias Alego'
leftComponent={<Image source={require('../../../assets/images/play_grande.png')} style={imageStyle} resizeMode='contain'/>}
/>
}
constructor(props) {
super(props);
this.renderItem = this.renderItem.bind(this);
this.keyExtractor = this.keyExtractor.bind(this);
this.renderContent = this.renderContent.bind(this);
this.navigateToNoticias = this.navigateToNoticias.bind(this);
this.carregarMaisNoticias = this.carregarMaisNoticias.bind(this);
this.onRefresh = this.onRefresh.bind(this);
}
componentDidMount() {
this.props.carregarNoticias(this.props.pagina);
}
renderItem({item}) {
return (
<NoticiaListItem noticia={item} abrirNoticia={this.navigateToNoticias} />
);
}
keyExtractor(item) {
return item.id.toString();
}
navigateToNoticias(noticia) {
this.props.navigation.navigate('NoticiasExibir', { id: noticia.id });
}
onRefresh() {
console.log('onRfresh');
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
}
renderContent() {
const { noticias, carregandoNoticias, erroNoticias } = this.props;
if(noticias.length === 0 && carregandoNoticias) {
return (
<View style={styles.containerCenter}>
<ActivityIndicator size="large" color={colors.verde}/>
</View>
);
}
if(erroNoticias) {
return (
<View style={styles.containerCenter}>
<Text style={styles.message}>{erroNoticias}</Text>
<TouchableOpacity hitSlop={hitSlop15}>
<Text>Recarregar</Text>
</TouchableOpacity>
</View>
)
}
return (
[<TextInput
style={styles.textInput}
placeholder='Pesquise'
key='pesquisa'
underlineColorAndroid='transparent'
/>,
<FlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>]
)
}
render() {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
{this.renderContent()}
</View>
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return {
noticias: state.intranet.noticias,
pagina: state.intranet.pagina,
erroNoticias: state.intranet.erroNoticias,
carregandoNoticias: state.intranet.carregandoNoticias
}
}
function mapDispatchToProps(dispatch) {
return {
carregarNoticias: (pagina, recarregar) => dispatch(ActionCreator.carregarNoticias(pagina, recarregar))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NoticiasScreen);
No idea what's going on. Any help is appreciated.
EDIT:
I fixed it somehow. I added the onMomentScrollBegin prop to prevent my flatList from rendering twice on Render, and that fixed this issue.
here is what I added:
constructor(props) {
super(props);
...
this.onRefresh = this.onRefresh.bind(this);
this.onMomentumScrollBegin = this.onMomentumScrollBegin.bind(this);
this.onEndReachedCalledDuringMomentum = true; //PUT THIS HERE
}
onRefresh() {
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
if(!this.onEndReachedCalledDuringMomentum){
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
this.onEndReachedCalledDuringMomentum = true;
}
}
onMomentumScrollBegin() {
this.onEndReachedCalledDuringMomentum = false;
}
render() {
<OptimizedFlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onMomentumScrollBegin={this.onMomentumScrollBegin} //PUT THIS HERE
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>
}

How to dynamically add a text input in React Native

How can I add a text input in React Native with the click of a button? For example, I would press the "+" button and it would add a text input at the bottom of the View.
EDITED:
Here is my code (deleted all the irrelevant stuff). Not working for some reason. Clicking the button doesn't do anything.
import React, { Component, PropTypes } from 'react';
import { StyleSheet,NavigatorIOS, Text, TextInput, View, Button,
TouchableHighlight, TouchableOpacity, ScrollView, findNodeHandle,
DatePickerIOS} from 'react-native';
import TextInputState from 'react-native/lib/TextInputState'
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {textInput: [],
date: new Date(),
}
}
addTextInput = (key) => {
let textInput = this.state.textInput;
textInput.push(<TextInput key={key} />);
this.setState({ textInput })
}
render(){
return(
<View>
<Button title='+' onPress={() =>
this.addTextInput(this.state.textInput.length)} />
{this.state.textInput.map((value, index) => {
return value
})}
</View>
)
}
}
this is an example for that :
import React, { Component } from 'react';
import { AppRegistry, View, Text, Button, TextInput} from 'react-native';
class App extends Component {
constructor(props){
super(props);
this.state = {
textInput : []
}
}
addTextInput = (key) => {
let textInput = this.state.textInput;
textInput.push(<TextInput key={key} />);
this.setState({ textInput })
}
render(){
return(
<View>
<Button title='+' onPress={() => this.addTextInput(this.state.textInput.length)} />
{this.state.textInput.map((value, index) => {
return value
})}
</View>
)
}
}
maybe that can help you :)
I have a solution that begins with a single text input. It has an "add" button that adds another text input just below the first. That new input keeps the "add" button, and all previous inputs above change to a "remove" button, with which, of course, the user can remove the corresponding view. I could only get it to work by handling state in a React Redux store, and so the code is spread out between too many different files to post here, but anyone interested can view it on GitHub or Snack.
I know this is an old post, but this is a problem I wish was answered when I first came here.
Here is example of dynamic add remove input
let obj = { text: '' }
this.state = {
attributeForm: [{ [1]: obj }],
duplicateAttributes: [1]
}
addAtributeRow() {
const { duplicateAttributes, attributeForm } = this.state;
let pushNumber = 1;
if (duplicateAttributes.length > 0) {
let max = Math.max(...duplicateAttributes);
pushNumber = max + 1
}
let arr = duplicateAttributes;
arr.push(pushNumber)
let obj = { text: '' }
this.setState({
attributeForm: [...attributeForm, { [pushNumber]: obj }]
})
this.setState({
duplicateAttributes: arr
})
}
deleteAttributeRow(number) {
const { duplicateAttributes, attributeForm } = this.state;
const index = duplicateAttributes.indexOf(number);
if (index > -1) {
duplicateAttributes.splice(index, 1);
let findedIndex;
for (let i = 0; i < attributeForm.length; i++) {
// var index = Object.keys(attributeForm[i]).indexOf(index);
if (Object.keys(attributeForm[i])[0] == number) {
findedIndex = i;
}
}
if (findedIndex > -1) {
attributeForm.splice(findedIndex, 1);
}
}
this.setState({
attributeForm: attributeForm,
duplicateAttributes: duplicateAttributes
})
}
render() {
const {attributeForm} = this.state;
{
duplicateAttributes.length > 0 && duplicateAttributes.map((item, index) =>
<View >
<Item style={GStyle.borderStyle} >
<Textarea placeholder="Text"
style={[GStyle.placeholder.text, { width: wp('90%') }]}
keyboardType="default"
autoCorrect={true}
autoCapitalize={'words'}
rowSpan={4}
value={attributeForm[index][item]['text']}
placeholderTextColor={GStyle.placeholder.color}
onChangeText={(text) => this.addAttributes(item, text, 'text')}
returnKeyLabel='done'
/>
</Item>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginHorizontal: wp('30%') }}>
{
<Button full rounded onPress={() => { this.deleteAttributeRow(item) }} >
<Icon name="minus" type="FontAwesome5" style={{ fontSize: wp('4%') }} />
</Button>
}
</View>
</View>
}
<Button full rounded onPress={() => { this.addAtributeRow() }} >
<Icon name="plus" type="FontAwesome5" style={{ fontSize: wp('4%') }} />
</Button>
}
If you want to do this with Hooks or Functional component then here is
the link of Expo
https://snack.expo.dev/#muhammadabdullahrishi/add-input
I have included how to add and delete Text Input
with hooks