When I use flatlist in RN, I find this error: Cannot read property 'loginname' of undefined - react-native-ios

I just want show a list of the site data to FlatList component,but I find an error:
TypeError: Cannot read property 'loginname' of undefined
therefore, I change '{item.author.loginname}' to '{item.author}', I get this error:
Unhandled JS Exception: Invariant Violation: Objects are not valid as a
React child (found: object with keys {loginname, avatar_url}). If you meant to render a collection of
Actually, property 'loginname' is exists. I am confused. Why?
Here is my code:
import React, { Component } from 'react';
import { Text, ScrollView, View, StyleSheet, FlatList,Image } from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
stories: [{id: 1, text: 'option1'}, {id: 2, text: 'option2'}, {id: 3, text: 'option3'}],
isFetching: false,
};
}
componentDidMount() {
this.fetchData();
}
onRefresh() {
console.log('refreshing')
this.setState({ isFetching: true }, function() {
this.fetchData()
});
}
fetchData() {
var url = "https://cnodejs.org/api/v1/topics?limit=1&mdrender=true"
fetch(url, {method: "GET"})
.then((response) => response.json())
.then((responseData) => {
this.setState({stories: responseData.data,isFetching: false})
console.log( responseData.data[0].author.loginname,responseData.data[0].visit_count)
})
.done();
}
_renderItem = ({item}) => (
<View>
<Text>{item.author.loginname}</Text>
</View>
)
render() {
return (
<ScrollView style={styles.container}>
<View style={styles.wrapper}>
<Text>Hello World!!!</Text>
<FlatList
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
data={this.state.stories}
keyExtractor={(item) => item.id}
renderItem={this._renderItem}
/>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
// alignItems: 'center',
// justifyContent: 'center',
paddingTop: 50,
backgroundColor: '#ecf0f1',
}
});

This issue occurs because of your initial state which is set in constructor. When first-time component try to render at that time it tacks story' state from constructor because of your API fetch tack some moment to complete call.
Just replace your constructore by following line.
constructor(props) {
super(props);
this.state = {
stories: [],
isFetching: false,
};
}

This problem occurred because your view loads first and then your api get called. So your stories is undefined.
Small Update to your code:
this.state = {
stories: null,
isFetching: false,
};
View inside render:
render() {
return (
<ScrollView style={styles.container}>
<View style={styles.wrapper}>
<Text>Hello World!!!</Text>
{this.state.stories !== null ?
<FlatList
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
data={this.state.stories}
keyExtractor={(item) => item.id}
renderItem={this._renderItem}
/>
: null
}
</View>
</ScrollView>
);
}
Output:
Hello World!!
atian25

Related

async function not workin

im trying to use async function to consume my api, but it simply doesent work, it didnt even console log anything, here is my code:
import React, { Component } from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import {NavigationContainer} from '#react-navigation/native'
import { SafeAreaView } from 'react-navigation';
import Profile from './Profile'
import api from '../services/api';
//import Home from './Mainpage'
const {heigth, width} = Dimensions.get('window')
here is just styles
styles = StyleSheet.create({
Container:{
flex:1,
justifyContent:"flex-end",
alignItems: "flex-end",
},
PlaceContainer:{
width: '100%',
maxHeight:200,
},
button:{
width:"10px",
height:"10px"
},
title:{
fontWeight:'bold',
fontSize: 20
},
place:{
width:width -40 ,
padding:20,
maxHeight:200,
marginHorizontal: 20,
marginVertical: 8,
backgroundColor: '#0ff'
},
separator:{
flex: 1,
height: StyleSheet.hairlineWidth,
backgroundColor: '#8E8E8E',
}
})
here I created a class to make everything
export default class Dermatologistas extends Component{
state ={
MessageError: null,
users: []
}
that is not working, it isnt even console logging
getUserList = async () => {
try {
const response = await api.get('/auth/list');
const { users } = response.data;
this.setState({ users });
} catch (response) {
this.setState({ errorMessage: response.data.error });
}
};
just rendered to test
render(){
const users = this.state.users
in this console.log it return Array []
console.log(users)
return(
<View>
{this.state.users.map(user => (
<View key={user._id} style={{marginTop: 15}}>
<Text>{user.title}</Text>
<Text>{user.speciality}</Text>
</View>
))}
</View>
)
}
}
Where's the call to getUserList?
Also, to your get method you should include the promise method then() which followed by a catch() method.
To summarize, your call should look like that:
fetch(url)
.then(function() {
})
.catch(function() {
});

send image taken from ReactNativeCamera to Nodejs backend that has multer?

I have a frontend with React native and I implemented the following:
import React, {Component} from 'react';
import Config from 'react-native-config';
import {
Button,
Alert,
Image,
Text,
View,
StyleSheet,
TouchableOpacity,
Platform,
CameraRoll,
} from 'react-native';
import {Container, Content, Icon} from 'native-base';
import {RNCamera} from 'react-native-camera';
import {SubHeader} from '../../components/headers';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: 'black',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20,
},
});
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
imageUri: null,
};
}
test = async () => {
if (this.camera) {
const data = await this.camera.takePictureAsync({
quality: 0.5,
base64: true,
});
const photo = {
name: data.fileName,
type: data.type,
uri:
Platform.OS === 'android'
? data.uri
: data.uri.replace('file://', ''),
};
this.setState({imageUri: data.uri}); //preview
const fdata = new FormData();
fdata.append('file', photo);
try {
let response = await fetch(`${Config.BASE_URL}/scan`, {
method: 'POST',
body: JSON.stringify(fdata),
//body: fdata //this launches a connection error
});
const res = await response.json();
return res;
} catch (error) {
console.error(error);
}
} else {
Alert.alert('debug', 'There is no camera');
}
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.off}
captureAudio={false}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
onGoogleVisionBarcodesDetected={({barcodes}) => {
console.log(barcodes);
}}
/>
<View>
<TouchableOpacity
onPress={this.test.bind(this)}
style={styles.capture}>
<Icon type="FontAwesome" ios="camera" android="camera" />
</TouchableOpacity>
</View>
</View>
//preview
<Image
style={{width: 66, height: 58}}
source={{
uri: this.state.imageUri,
}}
/>
</View>
);
}
}
On the backend with nodeJs express and multer:
app.post('/scan', uploader.upload.single('file'), ScanController.scan);
Multer is well implemented because it is working with postman and with frontend web application fucntionalities.
the image is shown on the android device, but I am getting always an undefined object on the backend, i do not know how to send this because is on base 64, How can I send it or receive correctly?
There is an open issue See here on react native about this, but I found a nice solution using rn-fetch-blob
as you may see in the documentation, you can implement the following:
First, delete the FormData instance, then change the fetch for RNFetchBlob.fetch
Code as follows
upload = async () => {
let ret = await RNFetchBlob.fetch(
'POST',
`${Config.BASE_URL}/scan`,
{
'Content-Type': 'multipart/form-data',
},
[
{
name: 'file',
filename: Date.now() + '.png',
type: 'image/png',
data: RNFetchBlob.wrap(data.uri),
},
],
);
return ret;
};
As you see, there is an array, where you can add the objects.
Multer implementation and RNCamera stays the same.
Hope this could be useful to someone!

Cannot read property 'length' of undefined - React tutorial

I'm doing a simple tutorial about React and Node, by building a CRUD app.
TypeError: Cannot read property 'length' of undefined
Error in this file: PostsManager.js:102
return (
<Fragment>
<Typography variant="display1">Posts Manager</Typography>
{this.state.posts.length > 0 ? (
<Paper elevation={1} className={classes.posts}>
<List>
This is my file PostsManager.js, it come from this tutorial https://developer.okta.com/blog/2018/07/10/build-a-basic-crud-app-with-node-and-react
import React, { Component, Fragment } from 'react';
import { withAuth } from '#okta/okta-react';
import { withRouter, Route, Redirect, Link } from 'react-router-dom';
import {
withStyles,
Typography,
Button,
IconButton,
Paper,
List,
ListItem,
ListItemText,
ListItemSecondaryAction,
} from '#material-ui/core';
import { Delete as DeleteIcon, Add as AddIcon } from '#material-ui/icons';
import moment from 'moment';
import { find, orderBy } from 'lodash';
import { compose } from 'recompose';
import PostEditor from '../components/PostEditor';
const styles = theme => ({
posts: {
marginTop: 2 * theme.spacing.unit,
},
fab: {
position: 'absolute',
bottom: 3 * theme.spacing.unit,
right: 3 * theme.spacing.unit,
[theme.breakpoints.down('xs')]: {
bottom: 2 * theme.spacing.unit,
right: 2 * theme.spacing.unit,
},
},
});
const API = process.env.REACT_APP_API || 'http://localhost:3001';
class PostsManager extends Component {
state = {
loading: true,
posts: [],
};
componentDidMount() {
this.getPosts();
}
async fetch(method, endpoint, body) {
try {
const response = await fetch(`${API}${endpoint}`, {
method,
body: body && JSON.stringify(body),
headers: {
'content-type': 'application/json',
accept: 'application/json',
authorization: `Bearer ${await this.props.auth.getAccessToken()}`,
},
});
return await response.json();
} catch (error) {
console.error(error);
}
}
async getPosts() {
this.setState({ loading: false, posts: await this.fetch('get', '/posts') });
}
savePost = async (post) => {
if (post.id) {
await this.fetch('put', `/posts/${post.id}`, post);
} else {
await this.fetch('post', '/posts', post);
}
this.props.history.goBack();
this.getPosts();
}
async deletePost(post) {
if (window.confirm(`Are you sure you want to delete "${post.title}"`)) {
await this.fetch('delete', `/posts/${post.id}`);
this.getPosts();
}
}
renderPostEditor = ({ match: { params: { id } } }) => {
if (this.state.loading) return null;
const post = find(this.state.posts, { id: Number(id) });
if (!post && id !== 'new') return <Redirect to="/posts" />;
return <PostEditor post={post} onSave={this.savePost} />;
};
render() {
const { classes } = this.props;
return (
<Fragment>
<Typography variant="display1">Posts Manager</Typography>
{this.state.posts.length > 0 ? (
<Paper elevation={1} className={classes.posts}>
<List>
{orderBy(this.state.posts, ['updatedAt', 'title'], ['desc', 'asc']).map(post => (
<ListItem key={post.id} button component={Link} to={`/posts/${post.id}`}>
<ListItemText
primary={post.title}
secondary={post.updatedAt && `Updated ${moment(post.updatedAt).fromNow()}`}
/>
<ListItemSecondaryAction>
<IconButton onClick={() => this.deletePost(post)} color="inherit">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
</Paper>
) : (
!this.state.loading && <Typography variant="subheading">No posts to display</Typography>
)}
<Button
variant="fab"
color="secondary"
aria-label="add"
className={classes.fab}
component={Link}
to="/posts/new"
>
<AddIcon />
</Button>
<Route exact path="/posts/:id" render={this.renderPostEditor} />
</Fragment>
);
}
}
export default compose(
withAuth,
withRouter,
withStyles(styles),
)(PostsManager);
Try to replace
state = {
loading: true,
posts: [],
};
With:
this.state = {
loading: true,
posts: [],
};
If you can get the length using a console.log(this.state.posts.lenght). But when you run the code if length is undefined. Try this one,
return (
<Fragment>
<Typography variant="display1">Posts Manager</Typography>
{ (this.state && this.state.posts && this.state.posts.length) > 0 ? (
<Paper elevation={1} className={classes.posts}>
<List>

firebase data to comparison in render

I need help to compare the firebase data (if)and render the right component.
Any help would be appreciated.
export default class EstoqueScreen extends Component {
constructor(props){
super(props);
var id=firebase.auth().currentUser.uid;
firebase.database().ref().child('users/'+id).once('value').then((snapshot) => {
var tipoUsuario=snapshot.val().tipo
});
}
render(){
console.log(this.tipoUsuario);
if(this.tipoUsuario==='admin'){
return(
<View>
<EstoqueAdminScreen/>
</View>
)
}else if(this.tipoUsuario==='vendedor'){
return(
<View>
<EstoqueVendScreen/>
</View>
)
}
}
}
export default class EstoqueScreen extends Component {
constructor(props){
super(props);
state = {
loading: true,
tipoUsuario: null,
}
}
componentDidMount(){
var id=firebase.auth().currentUser.uid;
firebase.database().ref().child('users/'+id).once('value').then((snapshot) => {
this.setState({ tipoUsuario: snapshot.val().tipo, loading: false })
});
}
render(){
console.log(this.state.tipoUsuario);
if(this.state.loading){
return (
<View>Loading</View>
)
}
if(this.state.tipoUsuario==='admin'){
return(
<View>
<EstoqueAdminScreen/>
</View>
)
}else if(this.state.tipoUsuario==='vendedor'){
return(
<View>
<EstoqueVendScreen/>
</View>
)
}
return (
<View>No hay tipo</View>
)
}
}

React Native and node js - fetching input values and using them in controllers

I have a react native component that has username and password text input fields. I want the values which are entered to be available in the node js controller to query my db. I am not able to use AsyncStorage as I cannot import AsyncStorage inside controller. How do I make this work? Please help.
My login screen:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Image,
TextInput,
Alert,
Navigator,
TouchableHighlight,
BackAndroid,
ScrollView,
AsyncStorage,
} from 'react-native';
var _navigator;
import Container from './Container';
import Button from './Button';
import Label from './Label';
import ImageContainer from './ImageContainer';
import RegisterView from './RegisterView';
export default class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
userName: '',
userPass: '',
error:null,
};
}
_navigate(){
this.props.navigator.push({
name: 'RegisterView', // Matches route.name
})
}
_handleAdd = () => {
if((this.state.userName)!=="" && (this.state.userPass)!=="" ){
const data = {
username: this.state.userName,
password: this.state.userPass
}
// Serialize and post the data
const json = JSON.stringify(data)
fetch('http://10.0.2.2:3000/users/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: json
})
.then((response) => response.json())
.then((res) => {
if (res.error) {
alert(res.error)
} else {
this.props.navigator.push({
name: 'CheckList', // Matches route.name
})
}
})
.catch(() => {
alert('There was an error logging in.');
})
.done()
}
else{
alert('Cannot be empty!');
}
}
render() {
return (
<ScrollView style={styles.scroll}>
<ImageContainer>
<Image
style={{width:110,height: 110, justifyContent: 'center',
alignItems: 'center'}}
source={require('./Logo.png')}>
</Image>
</ImageContainer>
<Container>
<TextInput
placeholder="Username"
style={styles.textInput}
onChangeText={(text) => this.setState({userName:text})}
autoCapitalize="none"
autoCorrect={false}
onSubmitEditing={(event) => {
const{userName}=this.state.userName;
const{onSubmitEditing}=this.props;
if(!userName) return
onSubmitEditing(userName)
this.refs.SecondInput.focus();
}}
/>
<TextInput
placeholder="Password"
ref='SecondInput'
secureTextEntry={true}
onChangeText={(text) => this.setState({userPass:text})}
style={styles.textInputPass}
/>
</Container>
<Container>
<Button
label="Sign In"
styles={{button: styles.primaryButton, label:
styles.buttonWhiteText}}
onPress={() => this._handleAdd()}
/>
</Container>
<Container>
<TouchableHighlight onPress={ () => this._navigate()}
underlayColor= 'transparent'>
<Text style ={styles.buttonBlackText}>New? Register
</Text>
</TouchableHighlight>
</Container>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
scroll: {
backgroundColor: '#FFFFFF',
padding: 30,
flexDirection: 'column'
},
buttonWhiteText: {
fontSize: 20,
color: '#FFF'
},
buttonBlackText: {
fontSize: 20,
marginTop: 20,
textAlign:'center',
color: '#000000'
},
textInput: {
fontSize: 20,
backgroundColor: '#FFF',
marginBottom: 20,
marginTop: 20
},
textInputPass: {
fontSize: 20,
backgroundColor: '#FFF',
marginBottom: 20,
},
primaryButton: {
backgroundColor: '#34A853'
},
});
Controller to query the username from db. For now, it's hardcoded.
import Profile from '../models/profile';
import moment from 'moment';
export const index = (req, res, next) => {
Profile.find({'username': 'John'}).lean().exec((err, profiles) =>
res.json(
// Iterate through each movie
{ profiles: profiles.map(profile => ({
...profile,
}))}
));
};

Resources