Undefined is Not an Object (evaluating 'this.state.data.confirmed.value') - javascript

I am new to React Native and practicing by creating a project which makes requests from a COVID-19 API. However, when I run my code, I get this error:
TypeError: undefined is not an object (evaluating 'this.state.data.confirmed.value')
import React from 'react';
import { View, Text } from 'react-native';
class TrackerScreen extends React.Component {
state = {
data: ''
}
componentDidMount = () => {
fetch('https://covid19.mathdro.id/api', {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
data: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<View>
<Text>
{this.state.data.confirmed.value}
</Text>
</View>
)
}
}
export default TrackerScreen;
I converted my componentDidMount to an arrow function as suggested by other members on an old thread but that did not get rid of the error. Does anybody have a solution to this issue? Any help would be appreciated. Thank you!

The first render will happen before the componentDidMount will be called. So, during the first render the data property from your state will be an empty string. And the code inside the render function is trying to access a nested prop. Either provide a more described state, like:
state = {
data: { confirmed: { value: null } }
}
or check the value inside the render function:
import React from 'react';
import { View, Text } from 'react-native';
class TrackerScreen extends React.Component {
state = {
data: ''
}
componentDidMount = () => {
fetch('https://covid19.mathdro.id/api', {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
data: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
const { data: {confirmed: { value } = { value: null } } } = this.state
return value ? (
<View>
<Text>
{value}
</Text>
</View>
): null;
}
}
export default TrackerScreen;
You probably don't want to render anything if the data isn't present at the moment, so an additional check for the value will handle that

Related

Render component after fetch React Native

I have an app where a user is prompted to enter a code, it calls an external service and returns data from the api. Currently I'm able to enter the code and call the api and get a response back, I can also poll it every 10 seconds and return the data (no sockets on api service).
I have two components, showLanding and showResult.
When the app initialises, I want to show showLanding, prompt for a code and return showResult.
The problem I'm having is I'm not able to get it to 'update' the view.
I have tried ReactDOM.render or render() within the getScreen function, I feel as though I've missed something glaringly obvious when reading the React Native doco.
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {showLanding: true, showResult: false}
}
render() {
if (this.state.showLanding) {
return (
<View
tapCode={this.state.tapCode}
style={styles.appContainer}>
{this.state.showLanding && <LandingComponent/>}
</View>
);
}
if (this.state.showResult) {
return (
<View
tapCode={this.state.tapCode}
style={styles.appContainer}>
{this.state.showResult && <ResultComponent/>}
</View>
);
}
}
}
export class LandingComponent extends React.Component {
getResult = (value) => {
this.state = {
tapCode: value.tapcode
}
console.log(this.state);
this.getScreen(this.state.tapCode);
}
getScreen = (tapcode) => {
setInterval(() => (
fetch('https://mywebservice.com/' + this.state.tapCode)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.then(this.state = {
showLanding: false,
showResult: true,
tapCode: tapcode,
resultResponse: this.responseJson
})
.catch((error) => {
console.error(error);
})
), 10000);
}
render() {
return (
<View style={styles.landingContainer}>
landing view, prompt for code
</View>
);
}
}
export class ResultComponent extends React.Component {
render() {
return (
<View style={styles.resultContainer}>
Show json output
</View>
);
}
}
There are plenty solutions, but you should definitely consider using a navigation library like react-navigation or react-native-router-flux to handle routing transitions between components.
My "dirty" suggestion would be: Let the App-Component render your Landing-Page and put the state property 'showResults' in there. Show the code-input if showResult is false, if not, show results.
export class LandingComponent extends React.Component {
constructor(props){
super(props);
this.state = {
showResults: false,
tapcode: null,
resultResponse
}
getResult = (value) => {
this.setState({tapCode: value.tapcode})
this.getScreen(this.state.tapCode);
}
getScreen = (tapcode) => {
setInterval(() => (
fetch('https://mywebservice.com/' + this.state.tapCode)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.then(function(res) {
this.setState({
showResult: true,
tapCode: tapcode,
resultResponse: res.json()})
return res;
})
.catch((error) => {
console.error(error);
})
), 10000);
}
render() {
return (
this.state.showResults ? <ResultComponent/> :
<View style={styles.landingContainer}>
call fetch here....
</View>
);
}
}
And please never mutate your state directly! Call setState() instead.
You need to move the API call up to App. Right now, showLanding is part of the App's state, but you're setting in the LandingComponent so you're not triggering a re-render.

React Warning: Can't call setState (or forceUpdate) on an unmounted component

I have 2 components:
Orders - fetch some data and display it.
ErrorHandler - In case some error happen on the server, a modal will show and display a message.
The ErrorHandler component is warping the order component
I'm using the axios package to load the data in the Orders component, and I use axios interceptors to setState about the error, and eject once the component unmounted.
When I navigate to the orders components back and forward i sometimes get an error in the console:
Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Orders (at ErrorHandler.jsx:40)
in Auxiliary (at ErrorHandler.jsx:34)
in _class2 (created by Route)
I tried to solve it by my previous case React Warning: Can only update a mounted or mounting component but here I can't make an axios token by the inspectors. Has anyone solved this issue before?
Here are my components:
Orders:
import React, { Component } from 'react';
import api from '../../api/api';
import Order from '../../components/Order/Order/Order';
import ErrorHandler from '../../hoc/ErrorHandler/ErrorHandler';
class Orders extends Component {
state = {
orders: [],
loading: true
}
componentDidMount() {
api.get('/orders.json')
.then(response => {
const fetchedOrders = [];
if (response && response.data) {
for (let key in response.data) {
fetchedOrders.push({
id: key,
...response.data[key]
});
}
}
this.setState({ loading: false, orders: fetchedOrders });
})
.catch(error => {
this.setState({ loading: false });
});
}
render() {
return (
<div>
{this.state.orders.map(order => {
return (<Order
key={order.id}
ingrediencies={order.ingrediencies}
price={order.price} />);
})}
</div>
);
}
}
export default ErrorHandler(Orders, api);
ErrorHandler:
import React, { Component } from 'react';
import Auxiliary from '../Auxiliary/Auxiliary';
import Modal from '../../components/UI/Modal/Modal';
const ErrorHandler = (WrappedComponent, api) => {
return class extends Component {
requestInterceptors = null;
responseInterceptors = null;
state = {
error: null
};
componentWillMount() {
this.requestInterceptors = api.interceptors.request.use(request => {
this.setState({ error: null });
return request;
});
this.responseInterceptors = api.interceptors.response.use(response => response, error => {
this.setState({ error: error });
});
}
componentWillUnmount() {
api.interceptors.request.eject(this.requestInterceptors);
api.interceptors.response.eject(this.responseInterceptors);
}
errorConfirmedHandler = () => {
this.setState({ error: null });
}
render() {
return (
<Auxiliary>
<Modal
show={this.state.error}
modalClosed={this.errorConfirmedHandler}>
{this.state.error ? this.state.error.message : null}
</Modal>
<WrappedComponent {...this.props} />
</Auxiliary>
);
}
};
};
export default ErrorHandler;
I think that's due to asynchronous call which triggers the setState, it can happen even when the component isn't mounted. To prevent this from happening you can use some kind of flags :
state = {
isMounted: false
}
componentDidMount() {
this.setState({isMounted: true})
}
componentWillUnmount(){
this.state.isMounted = false
}
And later wrap your setState calls with if:
if (this.state.isMounted) {
this.setState({ loading: false, orders: fetchedOrders });
}
Edit - adding functional component example:
function Component() {
const [isMounted, setIsMounted] = React.useState(false);
useEffect(() => {
setIsMounted(true);
return () => {
setIsMounted(false);
}
}, []);
return <div></div>;
}
export default Component;
You can't set state in componentWillMount method. Try to reconsider your application logic and move it into another lifecycle method.
I think rootcause is the same as what I answered yesterday, you need to "cancel" the request on unmount, I do not see if you are doing it for the api.get() call in Orders component.
A note on the Error Handling, It looks overly complicated, I would definitely encourage looking at ErrorBoundaries provided by React. There is no need for you to have interceptors or a higher order component.
For ErrorBoundaries, React introduced a lifecycle method called: componentDidCatch.
You can use it to simplify your ErrorHandler code to:
class ErrorHandler extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true, errorMessage : error.message });
}
render() {
if (this.state.hasError) {
return <Modal
modalClosed={() => console.log('What do you want user to do? Retry or go back? Use appropriate method logic as per your need.')}>
{this.state.errorMessage ? this.state.errorMessage : null}
</Modal>
}
return this.props.children;
}
}
Then in your Orders Component:
class Orders extends Component {
let cancel;
state = {
orders: [],
loading: true
}
componentDidMount() {
this.asyncRequest = api.get('/orders.json', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
})
.then(response => {
const fetchedOrders = [];
if (response && response.data) {
for (let key in response.data) {
fetchedOrders.push({
id: key,
...response.data[key]
});
}
}
this.setState({ loading: false, orders: fetchedOrders });
})
.catch(error => {
this.setState({ loading: false });
// please check the syntax, I don't remember if it is throw or throw new
throw error;
});
}
componentWillUnmount() {
if (this.asyncRequest) {
cancel();
}
}
render() {
return (
<div>
{this.state.orders.map(order => {
return (<Order
key={order.id}
ingrediencies={order.ingrediencies}
price={order.price} />);
})}
</div>
);
}
}
And use it in your code as:
<ErrorHandler>
<Orders />
</ErrorHandler>

POST http://localhost:3000/api/courses/[object%20Object]/units 404 (Not Found)

(Only my 3rd post here, so please excuse any blatant issues).
The following is my Unit component, a child of a Course component (courses has_many units).
import React from 'react';
import { connect } from 'react-redux';
import { getUnits, addUnit, updateUnit } from '../reducers/units';
import { Container, Header, Form } from 'semantic-ui-react';
class Units extends React.Component {
initialState = { name: ''}
state = { ...this.initialState }
componentDidUpdate(prevProps) {
const { dispatch, course } = this.props
if (prevProps.course.id !== course.id)
dispatch(getUnits(course.id))
}
handleSubmit = (e) => {
debugger
e.preventDefault()
debugger
const unit = this.state
const { dispatch } = this.props
if (unit.id) {
debugger
dispatch(updateUnit(unit))
} else {
debugger
dispatch(addUnit(unit))
this.setState({ ...this.initialState })
}
}
handleChange = (e) => {
const { name, value } = e.target
this.setState({ [name]: value })
}
units = () => {
return this.props.units.map( (unit, i) =>
<ul key={i}>
<li key={unit.id}> {unit.name}</li>
<button>Edit Module Name</button>
<button>Delete Module</button>
</ul>
)
}
render() {
const { name } = this.state
return (
<Container>
<Header as="h3" textAlign="center">Modules</Header>
{ this.units() }
<button>Add a Module</button>
<Form onSubmit={this.handleSubmit}>
<Form.Input
name="name"
placeholder="name"
value={name}
onChange={this.handleChange}
label="name"
required
/>
</Form>
</Container>
)
}
}
const mapStateToProps = (state) => {
return { units: state.units, course: state.course }
}
export default connect(mapStateToProps)(Units);
The following is its reducer:
import axios from 'axios';
import { setFlash } from './flash'
import { setHeaders } from './headers'
import { setCourse } from './course'
const GET_UNITS = 'GET_UNITS';
const ADD_UNIT = 'ADD_UNIT';
const UPDATE_UNIT = 'UPDATE_UNIT';
export const getUnits = (course) => {
return(dispatch) => {
axios.get(`/api/courses/${course}/units`)
.then( res => {
dispatch({ type: GET_UNITS, units: res.data, headers: res.headers })
})
}
}
export const addUnit = (course) => {
return (dispatch) => {
debugger
axios.post(`/api/courses/${course}/units`)
.then ( res => {
dispatch({ type: ADD_UNIT, unit: res.data })
const { headers } = res
dispatch(setHeaders(headers))
dispatch(setFlash('Unit added successfully!', 'green'))
})
.catch( (err) => dispatch(setFlash('Failed to add unit.', 'red')) )
}
}
export const updateUnit = (course) => {
return (dispatch, getState) => {
const courseState = getState().course
axios.put(`/api/courses/${course.id}/units`, { course })
.then( ({ data, headers }) => {
dispatch({ type: UPDATE_UNIT, course: data, headers })
dispatch(setCourse({...courseState, ...data}))
dispatch(setFlash('Unit has been updated', 'green'))
})
.catch( e => {
dispatch(setHeaders(e.headers))
dispatch(setFlash(e.errors, 'red'))
})
}
}
export default (state = [], action) => {
switch (action.type) {
case GET_UNITS:
return action.units;
case ADD_UNIT:
return [action.unit, ...state]
case UPDATE_UNIT:
return state.map( c => {
if ( c.id === action.unit.id )
return action.unit
return c
})
default:
return state;
}
};
Note: My reducer is working for my getUnits and rendering the units properly.
Note also: when I try to submit a new unit, it ignores all of the debuggers in my handleSubmit and the debuggers in my addUnits (in the reducer), but somehow renders the flash message of "Failed to add units".
Then the console logs the error seen in the title of this post.
I raked my routes and my post is definitely supposed to go to the route as it is.
I have tried passing in the unit and the course in various ways without any change to the error.
How can it hit the flash message without hitting any of the debuggers?
How do I fix this [object%20Object]issue?
Thanks in advance!
The variable course in the following line
axios.get(`/api/courses/${course}/units`)
is an object. When you try to convert an object to a string in JavaScript, [object Object] is the result. The space is then converted to %20 for the URL request.
I would look at the contents of the course variable. Likely, what you actually want in the URL is something inside of course. Perhaps course.id.
If you are still having issues, you'll need to explain what value should go in the URL between /courses/ and /units, and where that data exists.
You are invoking addUnit and updateUnit with a parameter that is equal to this.state in handleSubmit
const unit = this.state
addUnit(unit)
As this.state is of type object, it is string concatenated as object%20object.
getUnit works fine as the parameter passed there comes from the prop course. Check the value of state inside handleSubmit.

React Native - show data from an API in a <Text>

I'm using fetch() to get data from an external data and want to display it in my View.
console.log(responseJson) is displaying data from the API, but this.state.data in my <Text> is not showing it.
All shown in my app is "Temperatur: C".
Not getting any errors when I'm running it.
Is my this.state.data not the correct way to show it?
import React, { Component } from 'react'
import { StyleSheet, Text, View, Image, Button, Alert } from 'react-native';
class Wether extends Component {
state = {
data: ''
}
componentDidMount = () => {
fetch('https://opendata-download-metfcst.smhi.se/api/category/pmp2g/version/2/geotype/point/lon/16/lat/58/data.json',{
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
data: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<View>
<Text>
Temperatur: {this.state.data} C
</Text>
</View>
)
}
}
export default Wether
My console.log(responseJson) shows:
Object {
"parameters": Array [
Object {
"level": 0,
"levelType": "hmsl",
"name": "msl",
"unit": "hPa",
"values": Array [
1005,
],
},
As I have checked your code, seem everything is fine.
Just one thing you should consider is your response JSON Data Object.
You should not display your Response JSON-Data into display in Text Component
<View style={{flex:1, alignItems:'center', justifyContent:'center'}}>
<Text>
Temperatur: { this.state.data.approvedTime } C
</Text>
</View>
Please recheck your code again, hope it could help.
You should initialize state in the constructor, and you can us this.setState() everywhere else.
import React, { Component } from 'react'
import { StyleSheet, Text, View, Image, Button, Alert } from 'react-native';
class Wether extends Component {
constructor(props) {
super(props);
state = {
data: ''
}
}
componentDidMount = () => {
fetch('https://opendata-download-metfcst.smhi.se/api/category/pmp2g/version/2/geotype/point/lon/16/lat/58/data.json',{
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
data: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<View>
<Text>
Temperatur: {this.state.data} C
</Text>
</View>
)
}
}
export default Wether

ReactJS TypeError: undefined is not a function (near '...this.state.data.map...')

I am new at React and trying to learn it. I am getting data from API and I will use the data. It returns money rates based on 'USD'. I am gonna use that data for convert money but I am getting this error: Error here
I don't know what the problem is.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor (props) {
super(props)
this.state = {data: 'false'};
}
componentDidMount(){
this.getData();
}
getData = () => {
fetch("https://openexchangerates.org/api/latest.json?app_id=88a3d2b24b174bf5bec485533a3bca88")
.then(response => {
if (response.ok) {
return response;
} else {
let errorMessage =
'${response.status(${response.statusText})',
error = new Error(errorMessage);
throw(error);
}
})
.then(response => response.json())
.then(json =>{
console.log(json);
this.setState({ data: json.data })
});
}
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">React App</h1>
</header>
{
this.state.data &&
this.state.data.map( (item, key) =>
<div key={key}>
{item}
</div>
)}
</div>
);
}
}
export default App;
Thanks for your time.
Set your initial data property in the state to an empty array [].
A stringified 'false' evaluates to true which is why it tries to call the map function, but string doesn't have a map function.
constructor (props) {
super(props)
this.state = {data: []};
}
Working example here
Because this.state.data is not an array, it's a string.
Just use the below code and remove this.state.data && from render method.
constructor (props) {
super(props)
this.state = {
data: []
};
}

Categories

Resources