(React Native) Null is not an object - reactjs

This is the portion of the code React Native is having trouble rendering:
You input {this.state.zip}.
Im a beginner and I was following a tutorial in "Learning React Native" yet he code in the book is not working.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TextInput,
Image,
View
} from 'react-native';
class WeatherProject extends Component {
// If you want to have a default zip code, you could add one here
getInitialState() {
return ({
zip: ''
});
}
// We'll pass this callback to the <TextInput>
_handleTextChange(event) {
// log statements are viewable in Xcode,
// or the Chrome debug tools
console.log(event.nativeEvent.text);
this.setState({
zip: event.nativeEvent.text
});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
You input {this.state.zip}.
</Text>
<TextInput
style={styles.input}
onSubmitEditing={this._handleTextChange}/>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
borderWidth: 2,
height: 40
}
});
AppRegistry.registerComponent('WeatherProject', () => WeatherProject);
[enter image description here][1]

In ES6 Classes (one where you are extending Component rather than using createClass), initial states are set with this.state = {zip: ''} in constructor.
So it would-be
class WeatherProject extends Component {
constructor(props){
super(props);
this.state = {
zip: ""
}
}
}

getInitialState is typically used with React.createClass. For defining a component as class, the following code should be in constructor:
getInitialState() {
return ({
zip: ''
});
}
constructor:
constructor() {
super();
this.state = {
zip: ''
}
}

Related

react native function return value not showing

i am using react native code but some how code not working. please let me check how i can fix. i am getting value from cache and trying to return & show value.
i tried lot some how code not working if someone has any idea please let me know
import React, { memo } from 'react';
import { Text, View, StyleSheet, AsyncStorage } from 'react-native';
import { theme } from "../core/theme";
class Dashdata extends React.Component{
constructor(){
super();
this.getDataName = this.getDataName.bind(this);
this.state = {
displayname: ''
};
}
getDataName = () => {
const displayname = '';
console.log('getting value from cachedd');
const loginName = AsyncStorage.getItem('#kidssafety:displayname')
.then((result)=>{
console.log(result);
return (
<Text>{result}</Text>
)
});
}
render(){
return(
<View>
<Text style={styles.header}>Welcome Data {this.getDataName()}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
header: {
fontSize: 22,
color: theme.colors.primary,
fontWeight: "bold",
paddingVertical: 14,
flex: 1,
marginTop: 100,
width: '100%',
textAlign: 'left'
}
});
export default memo(Dashdata);
AsyncStorage returns a promise. So you have to wait until it resolves.
Use Async/await to fix your problem.
getDataName = async () => {
const loginName = await AsyncStorage.getItem('#kidssafety:displayname')
this.setState({
displayname: loginName
})
}
Now you can display your values inside render
<Text style={styles.header}>Welcome Data {this.state.displayname}</Text>
Important
Since you are using as getDataName as an arrow function, you don't have to bind it as
this.getDataName = this.getDataName.bind(this)
Hope this helps you. Feel free for doubts.
AsyncStorage.getItem returns a promise and by the time it resolves and returns a value, your render would have moved to the next line. Ideally you should store the result to the state and use it when ready. Then your component will look like.
import React, { memo } from 'react';
import { Text, View, StyleSheet, AsyncStorage } from 'react-native';
import { theme } from "../core/theme";
class Dashdata extends React.Component{
constructor(){
super();
this.getDataName = this.getDataName.bind(this);
this.state = {
displayname: '',
result: '' // add result here
};
}
getDataName = () => {
const displayname = '';
console.log('getting value from cachedd');
const loginName = AsyncStorage.getItem('#kidssafety:displayname')
.then((result)=>{
console.log(result);
this.setState({result}) // set result to state
});
}
render(){
const { result } = this.state
return(
<View>
{!!result && (<Text style={styles.header}>Welcome Data {result})</Text>}
</View>
)
}
}
const styles = StyleSheet.create({
header: {
fontSize: 22,
color: theme.colors.primary,
fontWeight: "bold",
paddingVertical: 14,
flex: 1,
marginTop: 100,
width: '100%',
textAlign: 'left'
}
});
export default memo(Dashdata);

Changing the style of "toggled" elements in a list, react-native

I'm having trouble changing the style of only one element in a list.
Below is my Main class, as well as StationDetails class, which is a component I've created to render the list elements one by one.
There is one line (Line 31) in the StationDetails I cant seem to figure out the problem with. I want to style the component based on whether or not the elements' ID is included in the activeStations list.
Here is the line:
style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
Here is my Main class
import React, { Component } from "react"
import axios from "axios"
import { Text, View, ScrollView } from "react-native"
import StationDetails from "./StationDetails"
class Main extends Component {
constructor(props) {
super(props)
this.state = { stations: [], pressStatus: false, activeStations: [] }
this.handleClick = this.handleClick.bind(this)
}
componentWillMount() {
axios
.get("https://api.citybik.es/v2/networks/trondheim-bysykkel")
.then(response =>
this.setState({ stations: response.data.network.stations })
)
}
handleClick() {
this.setState({ pressStatus: !this.state.pressStatus })
}
renderStations() {
return this.state.stations.map(stations => (
<StationDetails
activeStations={this.state.activeStations}
handleClick={this.handleClick}
pressStatus={this.state.pressStatus}
key={stations.id}
stations={stations}
>
{stations.name}
</StationDetails>
))
}
render() {
return (
<ScrollView style={{ flex: 1, marginTop: 20 }}>
{this.renderStations()}
</ScrollView>
)
}
}
export default Main
And here is my StationDetails component.
import React from "react"
import { Text, View } from "react-native"
import Card from "./felles/Card"
import CardSection from "./felles/CardSection"
import Button from "./felles/Button"
const StationDetails = ({
stations,
handleClick,
pressStatus,
activeStations
}) => {
const {
headerTextStyle,
leftPartStyle,
rightPartStyle,
pressedStyle,
buttonStyle
} = styles
return (
<Card style={{ flex: 1, flexDirection: "row" }}>
<CardSection style={leftPartStyle}>
<Text style={headerTextStyle}>
{stations.name}
</Text>
<Text>
Free bikes: {stations.free_bikes}
</Text>
</CardSection>
<CardSection style={rightPartStyle}>
<Button
onPress={() => {
if (!activeStations.includes(stations.id)) {
activeStations.push(stations.id)
} else {
activeStations.splice(activeStations.indexOf(stations.id), 1)
}
}}
style={
activeStations.includes(stations.id) ? pressedStyle : buttonStyle
}
>
Abonner
</Button>
</CardSection>
</Card>
)
}
const styles = {
textStyle: {
fontSize: 14
},
leftPartStyle: {
flex: 3,
flexDirection: "column",
justifyContent: "space-between"
},
rightPartStyle: {
flex: 1
},
pressedStyle: {
backgroundColor: "green"
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
buttonStyle: {
backgroundColor: "#fff"
}
}
export default StationDetails
You are trying to set state of Main.activeStations from StationDetails which is not advisable. Few things to keep in mind
Main's activeStations is in it's local component level state.
You shouldn't be trying to mutate that from a child component.
Since you assign mutated activeStations state to Main.activeStations from StationDetails, ReactNative (RN) doesn't find a difference in state in it's reconciliation process so does not re-render StationDetails.
We need RN to re-render StationDetails so that it will show the correct style for the buttons etc.
Documentation on setState
This is how I would do it
Let Main render StationDetails
Get a callback from StationDetails on which station was selected
Let Main take care of mutating it's own internal state (activeStations)
By doing it this way,
StationDetails is responsible only for rendering a list of stations given the props and nothing else. It's a dumb component that renders a list.
Main is responsible for handling it's own internal state
Heres the result :
Main.js class
import React, { Component } from 'react';
import axios from 'axios';
import { ScrollView } from 'react-native';
import StationDetails from './StationDetails';
export default class App extends Component {
constructor(props) {
super(props);
this.onSelectStation = this.onSelectStation.bind(this);
this.state = {
stations: [],
pressStatus: false,
activeStations: []
};
}
componentWillMount() {
axios.get('https://api.citybik.es/v2/networks/trondheim-bysykkel')
.then(response => this.setState({ stations: response.data.network.stations }));
}
onSelectStation(stationKey) {
const { activeStations } = this.state;
const activeStationsEdit = activeStations;
if (!activeStations.includes(stationKey)) {
activeStationsEdit.push(stationKey);
} else {
activeStationsEdit.splice(activeStations.indexOf(stationKey), 1);
}
this.setState({ activeStations: activeStationsEdit });
}
renderStations() {
return this.state.stations.map((stations) =>
<StationDetails
activeStations={this.state.activeStations}
pressStatus={this.state.pressStatus}
key={stations.id}
stations={stations}
stationId={stations.id}
onSelectStation={this.onSelectStation}
>
{stations.name}
</StationDetails>
);
}
render() {
return (
<ScrollView style={{ flex: 1, marginTop: 20 }}>
{this.renderStations()}
</ScrollView>
);
}
}
StationDetails class
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
const StationDetails = ({ stations, activeStations, stationId, onSelectStation }) => {
const { headerTextStyle, leftPartStyle, container, pressedStyle, buttonStyle } = styles;
return (
<View style={container}>
<View style={leftPartStyle}>
<Text style={headerTextStyle}>
{stations.name}
</Text>
<Text>
Free bikes: {stations.free_bikes}
</Text>
</View>
<TouchableOpacity
onPress={() => onSelectStation(stationId)}
style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
/>
</View>
);
}
const styles = {
container: {
flex: 1,
flexDirection: 'row',
marginBottom: 10
},
leftPartStyle: {
flex: 3,
flexDirection: 'column',
justifyContent: 'space-between'
},
pressedStyle: {
backgroundColor: 'green',
flex: 1,
},
headerTextStyle: {
fontSize: 18
},
buttonStyle: {
backgroundColor: 'skyblue',
flex: 1,
}
};
export default StationDetails;

How to properly update a react native swiper component when state changes?

I have a react native component that uses react-native-swiper module. One of the slides in the swiper contains text which is set in the component's state. In the component I also have a modal with form that changes the state's text when user tries to save the input data from the modal.
The question is: in my current implementation, every time I saves a new data, the swiper gets dragged to the last slide, and re-render the slides (the process is laggy). So I wonder what's the best way to update the slides more smoothly?
Below is my component:
'use strict';
import React from 'react';
import {
Dimensions,
StyleSheet,
View,
Text,
ScrollView,
AlertIOS,
AsyncStorage
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import Swiper from 'react-native-swiper';
import Button from 'react-native-button';
import { saveName, getName } from '../Utils/dataService';
import { showAlert } from '../Utils/alert';
import HeaderSection from './HeaderSection';
import { styles } from '../Styles/Styles';
import { renderPagination } from './renderPagination';
class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
currIndex: 0
};
}
componentDidMount() {
getName(val => this.setState({'name': val}));
}
showInputModal() {
AlertIOS.prompt(
'Enter New Doctor Name', null,
[
{
text: 'Save',
onPress: name => saveName(name, val => this.setState({'name': val}))
},
{ text: 'Cancel', style: 'cancel' }
]
);
}
render() {
return (
<View style={{flex: 1}}>
<Swiper ref='swiper' onIndexChanged={(index) => this.setState({'currIndex': index})}>
<View style={styles.slide}>
<Text style={styles.text}>Hello {this.state.name}</Text>
</View>
</Swiper>
<Button onPress={this.showInputModal.bind(this)}>
Customize
</Button>
</View>
);
}
}
export default MainView;
I had the similar problem. Then I tried rendering the Swiper (in your case) from the state and it optimizes the performance. I hope it will solve your problem too.
Just replace your class MainView with this one:
class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
currIndex: 0,
swiper: this.renderSwpier
};
}
componentDidMount() {
getName(val => this.setState({'name': val}));
}
renderSwpier(){
return(
<Swiper ref='swiper' onIndexChanged={(index) => this.setState({'currIndex': index})}>
<View style={styles.slide}>
<Text style={styles.text}>Hello {this.state.name}</Text>
</View>
</Swiper>
)
}
showInputModal() {
AlertIOS.prompt(
'Enter New Doctor Name', null,
[
{
text: 'Save',
onPress: name => saveName(name, val => this.setState({'name': val}))
},
{ text: 'Cancel', style: 'cancel' }
]
);
}
render() {
return (
<View style={{flex: 1}}>
{this.state.swiper.call(this)}
<Button onPress={this.showInputModal.bind(this)}>
Customize
</Button>
</View>
);
}
}

Uploading an image in react-native using react-native-image-picker

I have just initialized a basic react-native project and its running in the emulator. I have as well installed this package https://github.com/react-community/react-native-image-picker
and i am trying to upload an image. The code is simple as i have just added some code to handle image upload
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Button,
Text,
Image,
Alert,
View
} from 'react-native';
var ImagePicker = require('react-native-image-picker');
var options = {
title: 'Select Avatar',
customButtons: [
{name: 'fb', title: 'Choose Photo from Facebook'},
],
storageOptions: {
skipBackup: true,
path: 'images'
}
};
const onPressLearnMore = () => {
ImagePicker.launchImageLibrary(options, (response) => {
let source = { uri: response.uri };
this.setState({
avatarSource: source
});
});
//Alert.alert('Button has been pressed!');
};
export default class AwesomeProject extends Component {
constructor() {
super()
this.state = {
avatarSource: 'image.jpg'
}
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Button onPress={onPressLearnMore} title="Upload Image" color="#841584" accessibilityLabel="Learn more about this purple button" />
<Image source={this.state.avatarSource} style={styles.uploadAvatar} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
I get this error when i run this on the emulator
undefined is not a function (evaluating '_this.setState({
avatarSource: source
})')
<unknown>
index.android.bundle?platform=android&dev=true&hot=false&minify=false:1274:19
__invokeCallback
index.android.bundle?platform=android&dev=true&hot=false&minify=false:4818:21
<unknown>
index.android.bundle?platform=android&dev=true&hot=false&minify=false:4664:32
__guard
index.android.bundle?platform=android&dev=true&hot=false&minify=false:4753:11
invokeCallbackAndReturnFlushedQueue
index.android.bundle?platform=android&dev=true&hot=false&minify=false:4663:19
You should write the function onPressLearnMore inside your AwesomeProject component, and don't forget to bind in order to use this
export default class AwesomeProject extends Component {
constructor(){
...
this.onPressLearnMore = this.onPressLearnMore.bind(this)
}
onPressLearnMore(){
//you can use this.setState
}
render(){
...
}
}

How to make a dynamic datasource in listview react native?

I don't know what happen to my code. I'm trying to create a Todo list. I'm using listview. I have two components the Todo and the AddTodo.
main component
import React, { Component } from 'react';
import { View, Text, TextInput, StyleSheet, ListView } from 'react-native';
import * as moment from 'moment';
import TodoList from '../compoents/TodoList';
import {TodoModel, ITodo} from '../models/TodoModel';
interface TodoProps{
todo: TodoModel;
ter:string;
}
interface TodoState{
dataSource: any;
myTodo: Array<ITodo>;
}
export default class Todo extends React.Component <TodoProps,TodoState>{
constructor(props:TodoProps) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => true});
this.state = {
dataSource: ds,
myTodo: []
};
}
componentWillMount = () => {
console.log(this.state.myTodo);
let data = {
title: this.props.ter
};
if (this.props.ter) {
this.state.myTodo.push(data);
}
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.state.myTodo),
myTodo: this.state.myTodo
});
}
render() {
return (
<View style={{marginTop: 60}}>
<ListView enableEmptySections={true} dataSource={this.state.dataSource} renderRow={(rowData) => <TodoList data={rowData} /> } />
</View>
)
}
}
this will view the list of todo that I add from the form
AddTodo component
import * as React from "react";
import { Alert, View, Text, StyleSheet, TextInput, TouchableOpacity } from 'react-native';
import {TodoModel} from '../models/TodoModel';
import { Actions} from 'react-native-router-flux';
interface TodoState{
todoText?: string;
}
interface TodoProps{
text: string;
}
export default class AddTodo extends React.Component <TodoProps,TodoState> {
constructor(props:TodoProps){
super(props);
this.state = {
todoText:" "
};
}
handleSubmit = () => {
Actions.todo({ter: this.state.todoText});
}
render() {
return (
<View style={{margin: 128, marginLeft: 15, marginRight:15}}>
<Text>ADD</Text>
<TextInput autoCorrect={false} style={styles.input} placeholder='Todo' onChangeText={(text) => this.setState({todoText:text})} value={this.state.todoText} />
<TouchableOpacity onPress={this.handleSubmit.bind(this)}>
<Text style={styles.button}>Submit</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
button: {
backgroundColor: '#ccc',
color: 'white',
height: 40,
lineHeight: 30,
marginTop: 10,
textAlign: 'center',
alignSelf: 'stretch',
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
},
container: {
},
input: {
borderColor: '#ededed',
borderRadius: 5,
borderWidth: 1,
height: 37,
alignSelf: 'stretch',
}
})
the issue here. When the time I add one todo. I was successfully added to the myTodo array, but when I add the second todo. It removes the first todo, and show the second todo. It doesn't push and add to the array. Why it happen that way? but if you have tutorial on how to do it. It will more great to study for it. I'm very interested to learn react native.
update
export default class App extends React.Component<Props, State> {
render() {
return (
<Router>
<Scene key="root">
<Scene key="todo" component={Todo} title="Todo" initial={true} onRight={() => Actions.addTodo({text: 'Hello World!'})}
rightTitle="Add"/>
<Scene key="addTodo" component={AddTodo} title="Add Todo" backTitle="Cancel" />
</Scene>
</Router>
);
}
}
Let try my code, hope it can help you.
Your "push" will mutate the state directly and that could potentially lead to error prone code, even if you are "resetting" the state again afterwards
You can find more here: Correct modification of state arrays in ReactJS
Edited:
rewrite handleSubmit function
handleSubmit = () => {
Actions.pop({refresh :
{ter: this.state.todoText}
});
}
Change componentWillMount to componentWillReceiveProps
componentWillReceiveProps = (nextProps) => {
console.log(this.state.myTodo);
let data = {
title: nextProps.ter
};
let todoList = this.state.myTodo
if (nextProps.ter) {
todoList.push(data);
}
this.setState({
myTodo: todoList
});
}
You can see my answer for a very similar issue:
React Native: Child ListView will not update after element in parent is changed
So in fact, in your case, you may simply do this:
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => true});
export default class Todo extends React.Component <TodoProps,TodoState>{
constructor(props:TodoProps) {
super(props);
this.state = {
dataSource: ds.cloneWithRows([]), // or put your initial data here
myTodo: []
};
}
Then, every time you want to update the list, just use ds.cloneWithRows(NEW_DATA)
componentWillMount() {
// ...
this.setState({
dataSource: ds.cloneWithRows(this.state.myTodo),
myTodo: this.state.myTodo
});
}
One more thing, for react's default functions in component's life cycle, they are bound automatically, so don't need to use componentWillMount = () => {}, just use componentWillMount()
This is just the way you should do, if errors still happen, please show here, then we may figure them out together, thanks

Resources