React-native constructor and componentWillMount scope issue - javascript

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.

Related

react native - assigning array of object of state to a variable & updating value of specific element in array

I've been having a problem in accessing & assigning a state of array in React Native. My code is as follows:
export default class App extends Component{
constructor(props){
super(props)
this.state = {
boxChecked: [false, false],
sportCat: []
}
}
arrayHandler = (sportID, value) => {
this.setState({boxChecked[sportID]: !this.state.boxChecked[sportID]}) //3rd problem
if (value === true){
this.setState({sportCat: [...this.state.sportCat, sportID]})
} else if (value === false) {
var idx = this.state.sportCat.indexOf(sportID)
this.state.sportCat.splice(idx, 1)
} else {
console.log('boxstat undefined')
}
console.log(this.state.sportCat)
}
render() {
return (
<View style={styles.container}>
<CheckBox
sportID = { 1 }
value = { this.state.boxChecked[sportID] }
onChange = {(sportID, value) => {
this.arrayHandler(sportID, value)
console.log(value, 'value') //1st problem
console.log(sportID, 'sportID') //2nd problem
}} />
</View>
)
}
}
What I would like to ask are:
when I try to get the value of value, it returns undefined. Is the problem with how I assigned the sportID variable?
when I try to get the value of sportID, it returns Proxy {dispatchConfig: {…}, _targetInst: FiberNode, etc...
When I type the setState parameter this way, the first boxChecked[sportID] turns white instead of blue in Visual Studio, which means the syntax in considered invalid. Is my way to setState the array incorrect?
Your help is very much appreciated. Thank you!
onChange return the native element ,
use onValueChange it call the function with a boolean if it is checked or not
This is an example of the Checkbox Implementation
<View style={{ flexDirection: 'column'}}>
<CheckBox />
<View style={{ flexDirection: 'row' }}>
<CheckBox
value={this.state.checked}
onValueChange={() => this.setState({ checked: !this.state.checked })}
/>
<Text style={{marginTop: 5}}> this is checkbox</Text>
</View>
</View>
For setState issue, you need to update the whole array not direct assignments.
You also seem to be modifying the State without using setState here,
this.state.sportCat.splice(idx, 1)
Which is not a good thing to do.
I think you need to go through the docs more
By the way, you can manipulate the state directly from the component event
Try these:
export default class App extends Component{
constructor(props){
super(props)
this.state = {
boxChecked: [false, false],
sportCat: []
}
}
render() {
return (
<View style={styles.container}>
<CheckBox
value = { this.state.boxChecked[0] }
onValueChange = {() => { this.setState({ boxChecked[0]: !this.state.boxChecked[0] }) }} />
</View>
)
}
}
There is another way to solve your problem. You can define a Array of Object and each Object will contain the properties of a checkbox. This way you can dynamically define the checkboxes.
checkboxes = [
{ id: 1, isChecked:false, sportID: 20},
{ id: 2, isChecked:false, sportID: 25},
{ id: 3, isChecked:false, sportID: 30}
]
Below code shows the use of the above Object.
constructor(props){
super(props)
this.state = {
checkboxes: [
{ id: 1, isChecked:false, sportID: 20},
{ id: 2, isChecked:false, sportID: 25},
{ id: 3, isChecked:false, sportID: 30}
]
}
// Function To get The list of selected sports
getListOfSelectedSports(){
selectedSports = [];
this.state.checkBoxes.map((checkbox)=> { if(checkbox.isChecked) selectedSports.push(checkbox.sportID) })
return selectedSports;
}
renderCheckboxes(){
return this.state.checkBoxes.map((checkbox, index, checkBoxArray) =>
<CheckBox
key={checkbox.id}
style={{alignSelf:'center', margin:15}}
onValueChange={(toggleValue) => {
checkBoxArray[index].isChecked = toggleValue;
this.setState({checkBoxes:checkBoxArray});
}}
value={ checkbox.isChecked }
/>
)
}
render() {
return (
<View style={styles.container}>
{this.renderCheckboxes()}
.....
//Rest of you Code
)
}
Solved the problems.
This is how I managed to achieve multiple checkbox functionality.
import React, {Component} from 'react';
import {Platform,
StyleSheet,
Text,
View,
CheckBox,
TouchableOpacity
} from 'react-native';
export default class App extends Component{
constructor(props){
super(props)
this.state = {
sportID: [0,1,2,3],
boxChecked: [false, false, false],
sportCat: []
}
}
arrayHandler = (ID) => {
let boxChecked = [...this.state.boxChecked] // use spread operator
boxChecked[ID] = !boxChecked[ID] // to mutate the value
this.setState({boxChecked}, () => { // of specific array
const value = this.state.boxChecked[ID]
if (value === true){
this.setState({sportCat: [...this.state.sportCat, ID]})
} else if (value === false) {
var idx = this.state.sportCat.indexOf(ID)
if (idx > -1){
this.state.sportCat.splice(idx, 1)
}
}
console.log(this.state.boxChecked)
console.log(this.state.sportCat)
})
}
render() {
return (
<View style={styles.container}>
<CheckBox
value = { this.state.boxChecked[0] }
onValueChange = {() => {
this.arrayHandler(0)
}} />
<CheckBox
value = { this.state.boxChecked[1] }
onValueChange = {() => {
this.arrayHandler(1)
}} />
<CheckBox
value = { this.state.boxChecked[2] }
onValueChange = {() => {
this.arrayHandler(2)
}} />
<TouchableOpacity
style = {{ borderColor: 'black', borderWidth: 5, height: 50, width: 100}}
onPress = {() => {
console.log(this.state.sportCat)
console.log(JSON.stringify(this.state.sportCat))
}}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Turns out that setState() doesn't mutate the value of the state immediately, instead it creates a pending state transition. Accessing the value of state immediately after calling setState() method might return the unchanged value of the state. You must invoke a second callback function to setState(), as setState occurs asynchronously.
Moreover, mutating the value of specific index of array directly in the onValueChange component is not possible, you have to use the spread operator function.

React Native - LayoutAnimation: how to make it just animate object inside component, not whole component/view?

I'm trying to follow this example (code here) and employ LayoutAnimation inside my RN project (the difference from that example being that I just want to render my circles with no button that'll be pressed).
But when I've added LayoutAnimation, it's the whole view/screen/component that does the animation of 'springing in', not just the circles as I desire. Where do I have to move LayoutAnimation to in order to achieve just the circle objects being animated?
UPDATED AGAIN: Heeded bennygenel's advice to make a separate Circles component and then on Favorites, have a componentDidMount that would add each Cricle component one by one, resulting in individual animation as the state gets updated with a time delay. But I'm still not getting the desired effect of the circles rendering/animating one by one...
class Circle extends Component {
componentWillMount() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
}
render() {
return (
<View>
{ this.props.children }
</View>
);
}
}
class Favorites extends React.Component {
constructor(props) {
super(props);
this.state = {
circleCount: 0
}
}
componentDidMount() {
for(let i = 0; i <= this.props.screenProps.appstate.length; i++) {
setTimeout(() => {
this.addCircle();
}, (i*500));
}
}
addCircle = () => {
this.setState((prevState) => ({circleCount: prevState.circleCount + 1}));
}
render() {
var favoritesList = this.props.screenProps.appstate;
circles = favoritesList.map((item) => {
return (
<Circle key={item.url} style={styles.testcontainer}>
<TouchableOpacity onPress={() => {
Alert.alert( "Add to cart and checkout?",
item.item_name + "? Yum!",
[
{text: 'Yes', onPress: () => console.log(item.cust_id)},
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}
]
)}}>
<Image source={{uri: item.url}} />
</TouchableOpacity>
</Circle>
)});
return (
<ScrollView}>
<View>
<View>
{circles}
</View>
</View>
</ScrollView>
);
}
}
From configureNext() docs;
static configureNext(config, onAnimationDidEnd?)
Schedules an animation to happen on the next layout.
This means you need to configure LayoutAnimation just before the render of the component you want to animate. If you separate your Circle component and set the LayoutAnimation for that component you can animate the circles and nothing else in your layout.
Example
class Circle extends Component {
componentWillMount() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
}
render() {
return (<View style={{width: 50, height: 50, backgroundColor: 'red', margin: 10, borderRadius: 25}}/>);
}
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
circleCount: 0
}
}
componentDidMount() {
for(let i = 0; i < 4; i++) {
setTimeout(() => {
this.addCircle();
}, (i*200));
}
}
addCircle = () => {
this.setState((prevState) => ({circleCount: prevState.circleCount + 1}));
}
render() {
var circles = [];
for (var i = 0; i < this.state.circleCount; i++) {
circles.push(<Circle />);
}
return (
<View>
<View style={{flexDirection:'row', justifyContent:'center', alignItems: 'center', marginTop: 100}}>
{ circles }
</View>
<Button color="blue" title="Add Circle" onPress={this.addCircle} />
</View>
);
}
}
Update
If you want to use Circle component as your example you need to use it like below so the child components can be rendered too. More detailed explanation can be found here.
class Circle extends Component {
componentWillMount() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
}
render() {
return (
<View>
{ this.props.children }
</View>
);
}
}

React-native ListView with Navigation

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>
}
}

React-Native. Can only update a mounted or mounting component

import React, {Component} from 'react'
import {
Image,
AppRegistry,
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
ListView,
TouchableHighlight
} from 'react-native'
import ViewContainer from '../../components/ViewContainer';
import StatusbarBackground from "../../components/StatusbarBackground";
import firebase from 'firebase';
export default class Comments extends Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(['row 1', 'row 2']),
comment: '',
post: '',
}
this.componentDidMount();
this.componentDidMount = this.componentDidMount(this);
this.listenForItems = this.listenForItems.bind(this);
this.renderItem = this.renderItem.bind(this);
this._comment = this._comment.bind(this);
}
componentDidMount() {
var commentsRef = firebase.database().ref('/comments')
this.listenForItems(commentsRef);
}
listenForItems(commentsRef) {
var commentsRef = firebase.database().ref('/comments')
commentsRef.on('value', snap => {
var items = [];
snap.forEach((child) => {
if(child.val().post == this.state.post){
items.push({
post: child.val().post,
email: child.val().email,
comment: child.val().comment,
uid: child.key
});
}
});
var temp = []
var len = items.length;
for (var i = (len - 1); i >= 0; i--) {
temp.push(items[i]);
}
items = temp;
this.setState({
dataSource: this.state.dataSource.cloneWithRows(items)
});
});
}
_comment(post) {
var commentRef = firebase.database().ref('/comments');
var curr = firebase.auth().currentUser.email;
var newComment = commentRef.push();
newComment.set({
'post': post,
'email': curr,
'comment': this.state.comment,
});
}
renderItem(item) {
return (
<TouchableHighlight>
<View style={styles.post}>
<Text style={styles.email}>{item.email}{' said:'}</Text>
<Text style={styles.text}>{item.comment}</Text>
<Text style={styles.line}></Text>
</View>
</TouchableHighlight>
)
}
render() {
this.state.post = this.props.post
return (
<ViewContainer>
<StatusbarBackground />
<Image style={styles.title}
source={require('../../images/comment.png')}
/>
<TextInput
style={styles.textinput}
multiline={true}
placeholder = "Write something..."
onChangeText={(comment) => this.setState({comment: comment})}
value={this.state.comment}
placeholderTextColor = 'black'
underlineColorAndroid = 'white'
autoCorrect = {false}
/>
<View style={styles.comment}>
<TouchableOpacity onPress={() => {this._comment(this.props.post)}}>
<Text>Publish</Text>
</TouchableOpacity>
</View>
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderItem} />
</View>
</ViewContainer>
)
}
}
I'm making a social app with posts, likes and comments. When I want to see the comments of a post I'm rendering a list view with all the comments. The first try it works but if I want to see the comments of other post I get this error.
I think I have to use componentWillUnmount() but idk what code I have to put there. Any ideias? Thanks!
Remove this.componentDidMount() from your constructor, and remove the line where you bind it. It is called automatically in the React component lifecycle, which is available because you extend Component.
You should also have the componentWillUnmount function that should call something like:
this.commentsRef.off(...)
In order to remove the listener. In order to do that correctly, move the commentsRef callback to its own class function (call it onCommentsRefValue or something), and then you can do this.commentsRef.on('value', this.onCommentsRefValue ) and subsequently in componentWillUnmount you can call this.commentsRef.off('value', this.onCommentsRefValue )

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