Why is my react state data undefined in react? - reactjs

I am creating a react 360 application using an API to fetch data and then display it on a panel. Below I have the following code:
export class CryptoControlInformation extends React.Component {
state = {
details: {
description: '',
links: [],
}
};
componentDidMount() {
const CRYPTO_CONTROL_PATH = 'https://cryptocontrol.io/api/v1/public/details/coin/bitcoin?key=some_key';
fetch(CRYPTO_CONTROL_PATH)
.then(response => response.json())
.then(data => {this.setState({
details: {
description: data["description"],
links: [...this.state.details.links, ...data["links"] ]
}})
})
}
render() {
let links = this.state.details.links;
##################################
console.log(links[0])
{_id: "5b41d21fa8a741cf4187ec60", type: "reddit", name: "r/Bitcoin Reddit", link: "https://reddit.com/r/Bitcoin/"}
####################################
####################################
// Why is this returning undefined?
console.log(links[0]["name"]);
####################################
return (
<View>
<View style={styles.rightHeader}>
<Text style={{fontSize: 40}}>Information</Text>
</View>
<View>
<Text>{this.state.details.description}</Text>
</View>
<View>
<Text>{this.state.details.description}</Text>
</View>
</View>
)
}
}
I can't get the information inside my object and I don't understand why. I know that the information is there. I can console.log the object in its entirety but the individual pieces are undefined. What am I doing wrong? I've noticed in react that the state always has to be explicitly detailed.
For example, I found that I can't just do this:
export class Square extends React.Component {
state = {
info: undefined
};
componentDidMount() {
// grab information (pseudocode)
this.setState{info: data}
}
}
I have to actually map out the data which is annoying:
export class Square extends React.Component {
state = {
info: {
color: '',
height: '',
width: '',
}
};
componentDidMount() {
// grab information (pseudocode)
this.setState{info: {
color: data['red'],
heigth: data['height'],
width: data['width']
}
}
}
}
I'm thinking that this has something to do with my problem. Am I on the right track?

Standard timing problem - you didn't look for 'react undefined', right?
When component loads data render is called (minimum) twice - once at initial mounting (w/o data) and 2nd time when data arrives (setState forces new render)
console.log (in chrome) cheats you silently updating earlier message
you can use map - it works fine with initially empty array - or check if value is ready in jsx
{!links.length && <Text>{links[0]["name"]}</Text/>}
... conditionally call rendering function, return <Loading /> earlier, etc.
Using setState with function (Varun's comment) isn't required, it's safer (for some cases) but not obligatory

Related

React native child not receiving updated props value

I am new to react and making a little app to learn the library and way of thinking.
But I ran into a problem which has me completely stomped...
I have the root components that contains a list of tasks.
I pass these tasks to a custom component to render them into a groups like so:
<TasksView tasks={this.state.tasks}></TasksView>
Where this.state.tasks is an array containing all the current tasks.
But the problem is that my TaskView component never seems to receive these tasks...
See below for the complete code:
ROOT:
class App extends Component<{}, IAppState> {
constructor(props: any) {
super(props);
this.state = {
taskCategories: [],
tasks: [],
currentTask: {}
};
}
testing() {
this.setState((prev: IAppState) => {
return {
tasks: [...prev.tasks, {
index: 0,
timespan: [TimeTypes.Day, TimeTypes.Week, TimeTypes.Month, TimeTypes.Year][Math.round(Math.random() * 9)],
name: '',
repeatAmount: 0,
repeatDelay: 0,
category: {
id: 0,
color: "",
name: ""
},
onComplete: function (index: number): void {
throw new Error('Function not implemented.');
}
}]
}
});
}
render() {
console.log("tasks count in parent: ", this.state.tasks.length); //<-- console.log A
return (
<SafeAreaView style={styles.container}>
<TasksView tasks={this.state.tasks}></TasksView>
<TouchableOpacity
onPress={this.testing.bind(this)}
style={styles.createTask}
>
</TouchableOpacity>
</SafeAreaView>
)
}
};
TASKSVIEW:
class TasksView extends Component<TasksViewProps, any> {
taskGroups: TasksGroupData[];
stickyIndices: number[];
constructor(props: TasksViewProps) {
super(props);
console.log("props should be updated..."); //<-- console.log B
console.log(props.tasks.length); //<-- console.log C
this.taskGroups = [];
this.stickyIndices = [];
}
render(): ReactNode {
return [
<Text style={tasksViewStyle.title}>Your Goals</Text>,
<ScrollView
stickyHeaderIndices={this.stickyIndices}
showsVerticalScrollIndicator={false}
style={tasksViewStyle.scrollView}
>
{this.taskGroups.map((group: TasksGroupData, index: number) =>
<TasksGroup key={index} timespan={group.timespan} tasks={group.tasks}></TasksGroup>
)}
</ScrollView>
];
}
}
I have left out all the interface definitions and some helper functions since they would not be relevant to the problem at hand.
So what i would expect is every time console.log A gets executed console.log B and C would also be executed but this is not the case.
See here a screenshot of current console.log sequence.
If any additional information is required let me know so I can update the question.
The constructor is only run once when your component is first mounted, because React re-uses the same class instance for the lifetime of the component (see these docs). This is why you're not seeing your logging calls after they are initially run once.
For logging that runs on each render, you could move your console.logs into componentDidUpdate.

How to update state in componentWillMount and use that value in the render function in React Native?

I am collecting images from a dispatch call made to an action and mapping the returned response (images) into an array. When the data is finally returned I am storing this map by setting the state of the imgArray property. Unfortunately, when I do this I get a warning 'Can only update a mounted or mounting component' and my imgArray property on state is not available or updated in the render function (thus no images are being displaced). How can I get rid of this warning and get my data to the render function by first storing the data in the state?
componentWillMount function:
componentWillMount() {
this.props.dispatch(handleLoadProduct(this.props.product.id)).then((data) => {
let images = data.response.images.map(img => {
return 'https://b2b.martinsmart.com/productimages/' + img.original;
});
this.setState({imgArray: images});
});
}
Even though the isLoading property is getting set to true in the reducer it still shows up as false here and then the render is getting called before all the data is loaded.
render function:
render() {
const {isLoading, product} = this.props.products;
if (isLoading) {
return <Loader isVisible={true}/>;
}
return (
<View ref={(pc) => this.productCard = pc} style={{height: '100%', backgroundColor: '#D6D6D6'}}>
<Header/>
<View style={styles.wrapper}>
<View style={{height: '100%', borderRadius: 7}}>
<View style={styles.container}>
{this.state.imgArray &&
<Animated.Image
{...this.imgPanResponder.panHandlers}
key={'image' + this.state.imgIndex}
source={{uri: this.state.imgArray[this.state.imgIndex]}}
style={{left: this.imgXPos, width: '100%', height: '100%'}}
resizeMethod={'resize'}
resizeMode={'contain'}
/>
}
here I uploaded a video demonstrating this: https://youtu.be/tAbaq2IS4vY
Here is my reducer case:
case types.HANDLE_LOAD_PRODUCT: {
return {
...state,
isLoading: true,
product:{}
};
}
Here is my action function:
export function handleLoadProduct(productId) {
return (dispatch, getState) => {
if (getState().app.hasNetworkConnection) {
dispatch({
type: types.HANDLE_LOAD_PRODUCT
});
return API.getProduct(productId).then((response) => {
return dispatch({
type: types.HANDLE_LOAD_PRODUCT_SUCCESS,
response
});
});
}
};
}
Here is how I connect my products from the reducer:
function mapStateToProps(state) {
const {products} = state;
return {
products
};
}
export default connect(mapStateToProps)(ProductCard);
To fix the images not showing up issue, switch from componentWillMount to componentDidMount. In general, use componentDidMount for doing API calls.
The warning, however, might be due to many reasons. Try to use refs. If you are switching between screens, that might cause the issue too!
Check also the isMounted post in the React docs. I guess the component simply unmounts and then the state changes. Try to play around with console.log()s on componentWillUnmount() and figure out if the component unmounts before the this.setState() is called to change the state. A lot is going on in your code example, that's why it's a bit hard to say exactly what is causing the warning to show up. But these should give you a good clue.

Cannot access state inside fetch in React Native

In React Native, I have the following:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View, TouchableHighlight } from 'react-native';
import Immutable from 'immutable';
const styles = StyleSheet.create({
map: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: '20%',
},
});
export default class VirtualFenceBottom extends Component {
constructor(props) {
super(props);
this.state = { markers: this.props.markers };
}
populateMarkers = () => {
let markersVar = this.state.markers;
fetch('http://localhost:8080/virtualFence', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}}).then(function(response){
console.log('GET markers success');
// Parse it as JSON
parsedResponse = JSON.parse(response["_bodyInit"]);
console.log('response after JSON.parse: ',parsedResponse);
if (parsedResponse.length > 0) {
//update state here
console.log('parsedResponse in if statement: ',parsedResponse);
// this.setState({markers: parsedResponse});
} else {
console.log('There were no markers in db');
}
console.log('markersVar: ',markersVar);
// console.log('this.state.markers after setState: ',this.state.markers);
}).catch(function(error) {
console.log('GET markers error');
console.log("GET markers error: ",error);
});
};
render() {
return (
<View>
<TouchableHighlight onPress={this.populateMarkers}>
<Text style={styles.text}>Populate markers from DB</Text>
</TouchableHighlight>
</View>
);
}}
All goes as expected, except I cannot access state directly inside fetch. Strangely, I can access it inside the containing function with markerVar. It seems there is a particular issue with fetch itself.
My goal is to update the state with the response. None of the existing answers for similar questions seem to work in my case. What do I do?
UPDATE 1: Added the code for the entire component.
UPDATE 2: Fixed a misspelling of the parsedResponse variable that was causing one part of the error.
The callbacks that are called in response to fetch results are just unbuond functions.
Try this:
}}).then(function(response){
console.log('this', this); // see that 'this' is not
// what you expect it to be
Note, that function() { ...} in javascript creates closure that captures all local variables (including your marketsVar, but not _this__.
So, 'this' points into 'window' variable which doesn't have state (usually).
To fix this issue, you can
1) use fat arrow functions then and in catch handlers:(we're in 2018!, your toolchain handles it for sure):
fetch(...)
.then(() => {
this.setState(...) // this is defined. magic!
2) create alias to this - that is used sometimes:
var that = this;
fetch(...)
.then(function() {
that.setState(...); // that is defined in same way as marketsVar
})
3) bind your handlers manually but that's ugly as hell so i don't recommend it.
Be sure that this function exists inside of the component, otherwise it will not have access to the lexically scoped this.state method.
class MyComp extends React.Component {
state = { val: 0 }
myMethod = () => {
this.setState({ val: 2 })
}
}
Also consider whenever you pluck values from state, keeping the names consistent. For example:
const { markers } = this.state

Cannot read property 'propertyName' of undefined

I'm working on a project in react-native where I have troubles of accessing an element inside an object array by passing it as a prop where I want it to be used. Requirement is to get the name property out and set it to a text inside a flatlist.
The structure of my object array as follow.
[
{
"media1":[
{"name":"Lynn"},
{"name":"Michelle"},
{"name":"Carter"}
]
},
{
"media2":[
{"price":"23"},
{"price":"76"},
{"price":"39"}
]
}
]
This is how is pass this object array as a prop where I want it to be used
return (
<View>
<AlbumDetail data = {this.state.allData}/>
</View>
);
This is where I want it to be used
const AlbumDetail = (props) => {
return (
<View>
{console.log(props.data[0])} //Working
{console.log(props.data[0].media1[0].name)} //Not working
// Requirement as bellow
<Text>{wants to set the "name" here}</Text>
<Text>{wants to set the "price" here}</Text>
</View>
);
};
How can I achieve this ??
You might want to place two missing comma's.
One after:
{"name":"Michelle"}
And one after
{"price":"76"}
AlbumDetail has no way to know it has a property called data. You need to write AlbumDetail function as a React.Component class.
You are passing a JSON object into AlbumDetail, you need to call JSON.parse(data) before use it. UPDATE: .then(resp => resp.json()) is used to parse json.
Place console.log before return. The object you returned should be pure JSX components.
The code below should solve your problem:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const url =
'http://purelight-prod.appspot.com/api/user/v2/browse/homescreendata';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: undefined,
};
}
componentDidMount() {
fetch(url)
.then(resp => resp.json())
.then(respJson => {
this.setState({
data: respJson,
});
})
.catch(err => {
console.error(err);
});
}
render() {
return (
<View style={{ flex: 1 }}>
<TestView data={this.state.data} />
</View>
);
}
}
class TestView extends React.Component {
render() {
!!this.props.data && console.log(console.log(data[0].healer[0].healerid));
return (
<View>
<Text>Hello World!</Text>
</View>
);
}
}
Edit:
Use componentDidMount(), because we like to display something (loading icon, etc), and then update the View when data arrived.
This is an async task. The data has to be held until it arrived. I use !!this.props.data && ..., so it only displays when it is not undefined.
Since the API response is a relatively big package, it will be much easier to work with, if you use TypeScript and create an object class to parse it.
I don't think the API helper package provides correct response in your code.

How to access this.setstate in onShouldStartLoadWithRequest() of a WebView in React Native?

I'm really struggling to understand how to read and set this.state inside of functions called by WebView when doing specific operations. My end goal is to:
Show a activity indicator when the user clicks a link inside the webview
Perform certain actions based on the URL the user is clicking on
I'm very new to React, but from what I've learned, I should use () => function to bind this from the main object to be accessible inside the function.
This works on onLoad={(e) => this._loading('onLoad Complete')} and I can update the state when the page loaded the first time.
If I use onShouldStartLoadWithRequest={this._onShouldStartLoadWithRequest} I can see that it works and my console.warn() is shown on screen. this.state is of course not available.
However if I change it to onShouldStartLoadWithRequest={() => this._onShouldStartLoadWithRequest} the function doesn't seem to be executed at all, and neither this.state (commented in the code below) or console.warn() is run.
Any help is appreciated!
import React, { Component} from 'react';
import {Text,View,WebView} from 'react-native';
class Question extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
debug: 'Debug header'
};
}
render() {
return (
<View style={{flex:1, marginTop:20}}>
<Text style={{backgroundColor: '#f9f', padding: 5}}>{this.state.debug}</Text>
<WebView
source={{uri: 'http://stackoverflow.com/'}}
renderLoading={this._renderLoading}
startInLoadingState={true}
javaScriptEnabled={true}
onShouldStartLoadWithRequest={this._onShouldStartLoadWithRequest}
onNavigationStateChange = {this._onShouldStartLoadWithRequest}
onLoad={(e) => this._loading('onLoad Complete')}
/>
</View>
);
}
_loading(text) {
this.setState({debug: text});
}
_renderLoading() {
return (
<Text style={{backgroundColor: '#ff0', padding: 5}}>_renderLoading</Text>
)
}
_onShouldStartLoadWithRequest(e) {
// If I call this with this._onShouldStartLoadWithRequest in the WebView props, I get "this.setState is not a function"
// But if I call it with () => this._onShouldStartLoadWithRequest it's not executed at all,
// and console.warn() isn't run
//this.setState({debug: e.url});
console.warn(e.url);
return true;
}
}
module.exports = Question;
To access correct this (class context) inside _onShouldStartLoadWithRequest, you need to bind it with class context, after binding whenever this method will get called this keyword inside it will point to react class.
Like this:
onShouldStartLoadWithRequest={this._onShouldStartLoadWithRequest.bind(this)}
or use arrow function like this:
onShouldStartLoadWithRequest={this._onShouldStartLoadWithRequest}
_onShouldStartLoadWithRequest = (e) => {...}
Or like this:
onShouldStartLoadWithRequest={(e) => this._onShouldStartLoadWithRequest(e)}
Check this answer for more details: Why is JavaScript bind() necessary?

Resources