Cannot access value of a json object ? Cannot read property 'company_about' of undefined ? - json

This is my JSON
[
{
"id": 1,
"job_id": 1,
"company_profile": "Sales and Marketing",
"company_about": "Established in 1992 , it is a renouned marketing company",
"company_product": "Ford,Mustang,Beetle",
"key_skills": "commmunication,english,spanish,german",
"qualification": "High School,Masters",
"job_description": "Must be a Local of Mumbai",
"created_at": null,
"updated_at": null
}
]
I am trying to get its values.
this is my react code to log them.
public getJobDetails = (jobid: number) => {
const JobId = jobid;
fetch('http://127.0.0.1:8000/api/jobs/detail/' + JobId)
.then(response => response.json())
.then(
responseJson => {
console.log(responseJson);
this.setState({ details: responseJson });
},
() => {
console.log(this.state.details);
}
)
.catch(error => {
console.error(error);
});
}
public render() {
const { details } = this.state;
console.log(details);
console.log(details[0]);
The console.log(details[0]) returns
{id: 1, job_id: 1, company_profile: "Sales and Marketing", company_about: "Established in 1992 , it is a renouned marketing company", company_product: "Ford,Mustang,Beetle", …}
But why does console.log(details[0].company_profile) return undefined???
The Error it gives is :
TypeError: Cannot read property 'company_about' of undefined
can anyone help??

Use a conditional statement in your render so that if your request isn't complete and your state doesn't have details yet it doesn't load anything.
Edit --- Sample Code (not your application, but concept of what I mean)
import React, { Component, Fragment } from 'react';
export class App extends Component {
constructor(){
super()
this.state = {
data: [],
isLoading: true
}
}
componentWillMount(){
this.fetchDetails()
}
fetchDetails = () =>{
fetch('/some/url')
.then(res => res.json())
.then( => {
this.setState({data, isLoading: false})
})
}
render() {
return (
<Fragment>
{!this.state.isLoading && <ChildComponent data={this.state.data}} />}
</Fragment>
);
}
}

Try more logging, e.g.:
public getJobDetails = (jobid: number) => {
const JobId = jobid;
fetch('http://127.0.0.1:8000/api/jobs/detail/' + JobId)
.then(response => response.json())
.then(
responseJson => {
console.log(`Fetch resulted in ${JSON.stringify(responseJson)}`);
this.setState({ details: responseJson });
},
() => {
// This line is supposed to act as error handler, but there is no error handling
// See this - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Syntax
console.log(this.state.details);
}
)
.catch(error => {
console.error(`Fetch resulted in error ${JSON.stringify(error)}`);
});
}
public render() {
const { details } = this.state;
console.log('Rendering...');
console.log(`step 1. ${JSON.stringify(details)}`);
// let's see if details are not undefined and try next level
details && console.log(`step 2. ${JSON.stringify(details[0])}`);

Your fetch code is asynchronous and you don't have a default value set for this.state You can try a couple different options. You could redefine getJobDetails to return the promise rather than changing the state:
class MyComponent extends React.Component {
public getJobDetails = (jobid: number) => {
const JobId = jobid;
return fetch('http://127.0.0.1:8000/api/jobs/detail/' + JobId)
}
public render() {
this.getJobDetails().then(response => {console.log(response[0])})
}
}
Or you can set a default state
class MyComponent extends React.Component {
public state = {
details: [...]
}
}
EDIT
Performing a network request every render cycle is not very efficient, so it's probably not the best route to go. I also forgot a third option, conditional rendering like this:
class MyComponent extends React.Component {
state = { loading: true }
getJobDetails = (jobid: number) => {
fetch(...).then((response) => {
this.setState({details: response})
this.setState({loading : false})
})
}
render() {
return this.state.loading ? <h1>Loading...</h1> : <div>{this.state.deatils}</div>
}
}
Also you should not be converting your data to JSON if you want to access it as an Object

Related

Array with key of undefined coming through with React-Redux

I managed to get data from a my json.server into the desired component using the React-Redux methods, useSelector and useDispatch along with React's useEffect method.
What's confusing me is that even though the content of the array is there, the key for it is showing as undefined.
The console output:
Screenshot of console log
The component:
import React, { useEffect } from 'react';
import SetlistItem from './SetlistItem';
import { fetchSongs } from '../../actions/index'
import { useSelector, useDispatch } from 'react-redux';
const SetlistContainer = () => {
const songs = useSelector(state => state);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchSongs())
}, [dispatch])
console.log(songs)
return <div><SetlistItem /></div>
}
export default SetlistContainer;
The action:
export const fetchSongs = () => async dispatch => {
const response = await songEntries.get("/songEntries");
dispatch({ type: FETCH_SONGS, payload: response.data });
}
The reducer:
export default (state = {}, action) => {
switch (action.type) {
case FETCH_SONGS:
return { ...state, [action.payload.id]: action.payload };
case CREATE_SONG:
return { ...state, [action.payload.id]: action.payload };
default:
return state;
}
}
The db.json:
{
"songEntries": [
{
"id": 1,
"songTitle": "This is a song title",
"notes": "These are some notes"
}
]
}
I found the problem.
I needed to use the mapKeys method from the lodash library.
I should have made it clearer in the op that I'm trying to fetch an array of records, even though rather unhelpfully, I've only placed one element in the placeholder array.
Anyway the code is now this...
export default (state = {}, action) => {
switch (action.type) {
case FETCH_SONGS:
return { ...state, ..._.mapKeys(action.payload, 'id') };
case CREATE_SONG:
return { ...state, [action.payload.id]: action.payload };
default:
return state;
}
}

How can I turn a function used in many components into its own component which I can reuse across the app?

I have a fetch request used on multiple pages, and would like to turn it into a component to simply call in whenever it's needed. This is proving to be harder than I thought, and it's bring up a number of issues.
I have tried using the wrappedComponent function but not sure if that's the solution as it's still not working. It's now saying that the fetchPosts class constructor cannot be invoked without new.
const that = this;
fetch ('/testrouter')
.then (response => {
return response.json();
}).then(jsonData => {
that.setState({posts:jsonData})
}).catch(err => {
console.log('Error fetch posts data '+err)
});
}
This is what I want to turn into a component, so that I can just call it by it's name from another one inside componentDidMount. I have tried doing this:
function fetchPosts(WrappedComponent) {
class FetchPosts extends Component {
constructor(props) {
super(props)
this.state = {
posts: []
}
}
fetchAllPosts() {
const that = this;
fetch ('/testrouter')
.then (response => {
return response.json();
}).then(jsonData => {
that.setState({posts:jsonData})
}).catch(err => {
console.log('Error fetch posts data '+err)
});
}
render() {
return (<WrappedComponent
fetchAllPosts = {this.fetchAllPosts})
/>);
}
}
return FetchPosts;
}
export default fetchPosts
Then importing it and calling it with fetchPosts but it's not working.
I was hoping I would be able to create a component, add the code then import the component, but this is not working.
You might want to create a custom hook to do this:
useFetch.jsx
import React, { useState, useEffect } from 'react'
const useFetch = (url) =>
const [state, setState] = useState({ loading: true, data: null, error: null })
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setState(state => ({ ...state, loading: false, data }))
.catch(error => setState(state => ({ ...state, loading: false, error }))
},[])
return state
}
export default useFetch
MyComponent.jsx
import React from 'react'
import useFetch from './useFetch.jsx'
const MyComponent = () => {
const data = useFetch('/testrouter')
return (<>
{ data.loading && "Loading..." }
{ data.error && `There was an error during the fetch: {error.message}` }
{ data.data && <Posts posts={data.data}/> }
</>)
}

Returning an array to React component from fetch call in Redux

I'm having trouble returning an array to my React component from a fetch call from my Express server, that I set up in Redux.
I'm trying to just return the vitamins array from this json from Express:
router.get('/', function(req, res, next) {
vitamins: [
{
name: "Vitamin B2"
}
],
minerals: [
{
name: "Zinc"
}
]});
});
This is the fetch call and FETCH_VITAMINS_SUCCESS action in my actions.js.
export function fetchVitamins() {
return dispatch => {
return fetch("/users")
.then(res => res.json())
.then(micros => {
dispatch(fetchVitaminsSuccess(micros.vitamins));
return micros.vitamins;
})
};
}
export const FETCH_VITAMINS_SUCCESS = 'FETCH_VITAMINS_SUCCESS';
export const fetchVitaminsSuccess = vitamins => ({
type: FETCH_VITAMINS_SUCCESS,
payload: { vitamins }
});
This is my reducers.js where i'm trying to set the state to "micros.vitamins".
const initialState = {
micros: [],
};
function vitaminReducer(state = initialState, action) {
switch(action.type) {
case FETCH_VITAMINS_SUCCESS:
return {
...state.vitamins,
micros: action.payload
};
default:
return state;
}
}
This is my React component Vitamins.js where I'm importing fetchVitamins() and trying to pass the names of each vitamins to a menu dropdown in an option tag.
componentDidMount() {
this.props.fetchVitamins();
}
renderData() {
const { vitamins } = this.state.micros;
return vitamins.map((micro, index) => {
return (
<option value={micro.value} key={index}>{micro.name}</option>
)
})
}
render() {
return (
<select value={this.props.value}>
{this.renderData()}
</select>
)
}
const mapStateToProps = state => ({
micros: state.vitamins,
});
export default connect(mapStateToProps, { fetchVitamins })(Vitamins);
Right now I get back the error "TypeError: Cannot read property 'micros' of null", highlighting over my renderData() function.
It should be:
const { vitamins } = this.props.micros;
because you pass micros as props from redux. While you tried to access it from the state (which I guess was not initialized that's why it's null).
Another thing, you pass Object in payload:
export const fetchVitaminsSuccess = vitamins => ({
type: FETCH_VITAMINS_SUCCESS,
payload: { vitamins }
});
and set it as micros in your reducer:
switch(action.type) {
case FETCH_VITAMINS_SUCCESS:
return {
...state.vitamins,
micros: action.payload
};
However, your micros are initially an array which may lead to unexpected errors. Maybe you should change your initial state to something resembling the response like:
const initialState = {
micros: {
vitamins: []
},
};
This way const { vitamins } = this.state.micros; will always return some array - before and after the response.

Bug in mapStateToProps() from fetching json object in Redux

I'm trying to fetch data from my Express server in Redux, and mapping over the object to just use one array, called "vitamins". This is the json object.
router.get('/', function(req, res, next) {
vitamins: [
{
name: "Vitamin B2"
}
],
minerals: [
{
name: "Zinc"
}
]});
});
This is my action.js, where I'm creating the function fetchVitamins() to just fetch micros.vitamins.
export function fetchVitamins() {
return dispatch => {
return fetch("/users")
.then(res => res.json())
.then(micros => {
dispatch(fetchVitaminsSuccess(micros.vitamins));
return micros.vitamins;
})
};
}
export const FETCH_VITAMINS_SUCCESS = 'FETCH_VITAMINS_SUCCESS';
export const fetchVitaminsSuccess = vitamins => ({
type: FETCH_VITAMINS_SUCCESS,
payload: { vitamins }
});
This is my reducers.js
const initialState = {
micros: [],
};
function vitaminReducer(state = initialState, action) {
switch(action.type) {
case FETCH_VITAMINS_SUCCESS:
return {
...state,
micros: action.payload.vitamins
};
default:
return state;
}
}
This is my React component Vitamins.js where I'm importing fetchVitamins() and trying to pass the names of each vitamins to a menu dropdown in an option tag.
componentDidMount() {
this.props.dispatch(fetchVitamins());
}
renderData() {
const { vitamins } = this.state.micros;
return vitamins.map((micro, index) => {
return (
<option value={micro.value} key={index}>{micro.name}</option>
)
})
}
render() {
return (
<select value={this.props.value}>
{this.renderData()}
</select>
)
}
const mapStateToProps = state => ({
micros: state.micros.vitamins,
});
Right now when it renders, I get this error: "TypeError: Cannot read property 'vitamins' of undefined", highlighting over "micros: state.micros.vitamins,".
Am I calling and setting state correctly? If I set my initialState to micros: [], then setting the state to "state.micros.vitamins" should work, I thought.
because of you get the server data n vitamins objects so that data should be in vitamins:[], in that Format so that why state.macros.vitamins work.

user photo in tab navigation

In a react native project I am trying to set the user's profile photo as a tabBarIcon in tabNavigation. Below is how I am trying to retrieve the photo path and set it in the source for TabBarIcon.
First I have a token in AsyncStorage that gives me the username, email, or phonenumber of the user after login (works fine). This is in my constructor:
constructor(props) {
super(props)
this.state = {
Access: []
}
}
I set the Access in my state to a value in my AsyncStorage with getItem('Access') which i know works fine.
Now i have a function getProfilePhoto where I use fetch to get the profile photo.
getProfilePhoto = () => {
const { Access } = this.state.access;
fetch('http://urltofiletogetprofilephoto', {
method: 'POST',
headers: {
'Accept':'application/json',
'Content-Type':'application/json',
},
body: JSON.stringify({
Access:Access
})
}).then((response) => response.json())
.then((responseJson) => {
if(responseJson === 'NULL') {
console.log('../Images/NoPhoto.png');
} else {
console.log('../' + responseJson);
}
})
}
What I return from that file is:
$profilephoto = $row['ProfilePhoto'];
$profilephotoJson = json_encode($profilephoto);
echo $profilephotoJson;
That should return something like "Images/userprofilephoto.png". Now in navigationOptions I have this:
static navigationOptions = {
tabBarLabel: 'Profile',
tabBarIcon: ({ tintColor }) => (
<Image
source = {this.getProfilePhoto}
style={[styles.icon, {tintColor: tintColor}]}
/>
)
}
I thought calling the function would print the returned Image path, but when I run the app on my device I don't get an error but my tabBarIcon Image is just blank. I am new to react native and haven't worked with Json much I am hoping someone will be able to see something wrong that I am missing!
try
source={require(this.getProfilePhoto())}
However your function getProfilePhoto is not returning a path as you are using fetch.
Also navigationOptions is static, so this is not available.
You will need to access it via navigation params
static navigationOptions = ({ navigation }) => {
const { state } = navigation;
return {
tabBarLabel: 'Profile',
tabBarIcon: ({ tintColor }) => (
<Image
source = {state.params.getImage()}
style={[styles.icon, {tintColor: tintColor}]}
/>
)
}
}
componentWillMount() {
this.props.navigation.setParams({
getImage: () => {
this.getProfilePhoto();
},
});
}
getProfilePhoto () => {
//here you can get the path from this.props which would be added
//as before the component mounts
return this.props.profileImagePath; //set from redux connect
}
One downside with this is that if you want to update the image on the fly, you will need to call setParams again to force it to re-render the tab.
componentWillReceiveProps(nextProps) {
this.props.navigation.setParams({
getImage: () => {
this.getProfilePhoto();
},
});
}
I would have the action of getting the image separate to the component, and use Redux to connect to the latest image path. You can therefore set the Redux store triggered from another component.
You probably need to setState when your promise resolved by adding the data fetching request in the comoponentWillMount hook and make sure your image resides in the generated location relative to your component.
class UserProfile extends React.Component {
constructor(props) {
super(props)
this.state = {
Access: []
image: null
}
}
componentWillMount() {
this.getProfilePhoto();
}
getProfilePhoto = () => {
const { Access } = this.state.access;
fetch('http://urltofiletogetprofilephoto', {
method: 'POST',
headers: {
'Accept':'application/json',
'Content-Type':'application/json',
},
body: JSON.stringify({
Access:Access
})
}).then((response) => response.json())
.then((responseJson) => {
if(responseJson === 'NULL') {
console.log("../Images/NoPhoto.png");
} else {
this.setState({image: responseJson})
}
})
}
render() {
return (
this.state.image
?
<Image
source={require(this.state.image)}
style={this.props.style}
/>
:
null
)
}
}
static navigationOptions = {
tabBarLabel: 'Profile',
tabBarIcon: ({ tintColor }) => (
<UserProfile
style={[styles.icon, {tintColor: tintColor}]}
/>
)
}