user photo in tab navigation - json

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}]}
/>
)
}

Related

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

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

How to add a state into a api fetch()

Hi I need to add a state into my API fetch but struggling to see why it works when the state is an empty string but does not work if there is a string inside the state please view the examples
The idea is the user enters new text in an input which updates the state Search and then updates fetch url so they can search the database
Example Working Code (Notice the state Search is empty)
export default class ThirdScreen extends React.Component {
state = {
search: '',
image: ''
}
componentDidMount() {
this.fetchsa();
}
fetchsa = () => {
const {search} = this.state;
fetch(`https://xxx/search?q=moon&media_type=image`)
.then((response) => response.json())
.then((result) => this.setState({
image: result.collection.items[0].links[0].href
}))
}
Example not working Code
export default class ThirdScreen extends React.Component {
state = {
search: 'moon', //Notice this is not empty and now causes an error
image: ''
}
componentDidMount() {
this.fetchsa();
}
fetchsa = () => {
const {search} = this.state;
fetch(`https://xxx/search?q='${search}'&media_type=image`)
.then((response) => response.json())
.then((result) => this.setState({
image: result.collection.items[0].links[0].href
}))
}
The problem are the single quotes in your fetch URL:
const search = 'moon';
fetch(`https://xxx/search?q='${search}'&media_type=image`)
is NOT the same as
fetch(`https://xxx/search?q=moon&media_type=image`)
The API request goes through for 'moon' instead of moon and no results are found.
However this is ok:
fetch(`https://xxx/search?q=${search}&media_type=image`)
So:
Lose the single quotes around ${search}.
Handle an empty items array when no results are found.
For example:
fetch(`https://xxx/search?q=${search}&media_type=image`)
.then((response) => response.json())
.then((result) => result.collection.items.length > 0 && this.setState({
image: result.collection.items[0].links[0].href
}))
Try this:
export default class ThirdScreen extends React.Component {
state = {
search: 'moon', //Notice this is not empty and now causes an error
image: ''
}
componentDidMount() {
this.fetchsa();
}
fetchsa = () => {
const {search} = this.state;
fetch(`https://xxx/search?q='${search}'&media_type=image`)
.then((response) => response.json())
.then((result) => this.setState({
result.collection && result.collection.items[0] && result.collection.items[0].links[0] ?image: result.collection.items[0].links[0].href:null
}))
}

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.

How to get data (of my api json) in my object ( Redux, React )?

I not undestand everything with javascript etc, I want to get my data returned by ma action redux but i'have a problem with my code.
const mapStateToProps = state => {
const group = state.groupReducer.group ? state.groupReducer.group : [ ]
return {
group
}
how i can get my data ?
When I try with that:
const mapStateToProps = state => {
const group = state.groupReducer.group.data.data[0] ? state.groupReducer.group.data.data[0] : [ ]
return {
group
}
And my goal is map around group
renderGroup = group => {
return group.map((groups => {
<div key={groups.data.data.id}>
//
</div>
}))
}
Sagas.js
export function* loadApiDataGroup() {
try {
// API
const response = yield
call(axios.get,'http://localhost:8000/api/group');
yield put(loadGroup(response))
} catch (e) {
console.log('REQUEST FAILED! Could not get group.')
console.log(e)
}
}
Action.js
export function loadGroup(data){ return { type: LOAD_GROUP, data }};
export function creatGroup(data){ return { type: CREATE_GROUP, data}};
// reducer
export default function groupReducer( state= {}, action = {}){
switch (action.type){
case LOAD_GROUP:
return {
...state,
group: action.data
}
case CREATE_GROUP:
return {
...state
}
default:
return state
}
thank you to help me
Try
const mapStateToProps = state => ({
group: state.groupReducer.group || []
});
Then you can use this.props.group in the component. Even though you might only want one thing in mapStateToProps, it's usually not directly returned like that.
If group is the response of an API request, you need to unpack data first, this is done in your async action creator (you will want to use redux-thunk or something similar):
const getGroup = () => async (dispatch) => {
dispatch({ type: 'GET_GROUP_REQUEST' });
try {
const { data } = await axios.get('/some/url');
dispatch({ type: 'GET_GROUP_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'GET_GROUP_FAILURE', payload: error });
}
};