Content is being replaced instead of appended to in react native during pagination - react-native

Below is the main view screen which fetches the data, then gets it to the rendering component. For some reason the content is not being appended but rather replaced by the new 1.
export default class Questions extends React.Component {
constructor(props)
{
super(props);
this.state = {
data:[],
options_array: [],
last_id: 0,
username: '',
image_url:''
};
}
loadInitialState = async () => {
this.state.username = await AsyncStorage.getItem('user'); //This should be tranfered from the login
this.state.image_url = await AsyncStorage.getItem('image_url');
this.getData();
};
getData()
{
return fetch('url.com/endpoint', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.state.username,
last_id: this.state.last_id,
options_array: this.state.options_array
})
})
.then((response) => {
console.log(response);
return response.json()
})
.then((res) => {
this.setState({data: res.questions});
this.setState({last_id:res.last_id});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount()
{
this.loadInitialState().done();
}
render()
{
return (
<Content>
<TouchableOpacity onPress={()=>this.props.navigation.navigate("PostQuestion")}>
<Card transparent>
<CardItem>
<Left>
<Thumbnail small source={{uri: this.state.image_url}} />
<Body>
<Text style={styles.poster_username}>{this.state.username}</Text>
<Text style={styles.moto1}>Help others help you</Text>
</Body>
</Left>
</CardItem>
</Card>
</TouchableOpacity>
<QuestionsData data={this.state.data} navigation = {this.props.navigation}/>
<Button title='Load' onPress={()=>this.loadInitialState().done()}/>
</Content>
);
}
}
Below is where the rendering takes place
export default class QuestionsData extends React.Component
{
constructor(props)
{
super(props);
this.state =
{
navigation: null
}
}
componentDidMount()
{
this.state.navigation = this.props.navigation;
console.log(this.props)
}
questionList(props)
{
return props.data.map(function (questionData, index)
{
return renderQuestionContent(questionData, props)
}
);
}
render()
{
return this.questionList(this.props)
}
}

I managed to figure it out, i replaced this.setState({data: res.questions}); with this.setState({data: [ ...this.state.data, ...res.questions ]}); and then it worked just fine

Related

Populate view with data from web-service/API

I am trying to use data for an API in React Native to create multiple views.
I have used the code below but the page renders before the web service response so i keep this error:
Unhandled JS Exception: null is not an object (evaluating 'this.state.user.map')
export default class Component6 extends Component {
constructor(props) {
super(props);
this.state = {
user: null
}
}
componentDidMount() {
this.fetchUsers();
}
fetchUsers() {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((response) => {
this.setState({
});
});
}
render() {
contents = this.state.user.map(function (item) {
return (
<View key={item.id} style={styles.content}>
<Text>{item.email}</Text>
</View>
);
});
return (
{contents}
);
}
}
AppRegistry.registerComponent('Component6', () => Component6);
You can provide an 'if' statement, and also its better to move your contents outside of render so it's not redefining it on every render:
export default class Component6 extends Component {
constructor(props) {
super(props);
this.state = {
user: null
}
}
componentDidMount() {
this.fetchUsers();
}
fetchUsers() {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((response) => {
item = this.viewconstur(response),
this.setState({
user: item
});
});
}
renderContents = () => {
if (this.state.user) {
this.state.user.map((item) => (
<View key={item.id} style={styles.content}>
<Text>{item.email}</Text>
</View>
));
}
}
render() {
return (
<View>
{this.renderContents()}
</View>
);
}
}

How to set other first screen when I am logged in

How can I choose according value in AsyncStorage which screen should be displayed? I don't know why setting screen value 'Home' to InitialScreen variable doesn't work?
Once I log in login.js screen and I close app, after launching the app again I am navigated to login.js. But now I want to go to home.js screen.
Parent's file routes.js:
let InitialScreen
const RoutesNavigation = StackNavigator({
Login: { screen: Login },
Home: { screen: Home }
}, {
initialRouteName: InitialScreen,
navigationOptions: {
header: false,
}
});
export default class App extends Component {
constructor(props) {
super(props);
value = AsyncStorage.getItem('name');
if (value !== null) {
InitialScreen = 'Home'; //This doesn't change Initial screen!!!
console.log("JJJJJJJJJJJJJJJJJJ routes.js value !== null ");
}
}
render() {
return (
<RoutesNavigation />
);
}
}
This is login.js, where I store value from received json:
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
}
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.textInput} placeholder='Username'
onChangeText={(username) => this.setState({ username })}
underlineColorAndroid='transparent'
/>
<TextInput
style={styles.textInput} placeholder='Password'
onChangeText={(password) => this.setState({ password })}
secureTextEntry={true}
underlineColorAndroid='transparent'
/>
<TouchableOpacity
style={styles.btn}
onPress={this.login}>
<Text>Log in</Text>
</TouchableOpacity>
</View>
);
}
login = () => {
var formData = new FormData();
formData.append('userName', this.state.username);
formData.append('password', this.state.password);
fetch('http://....', {
method: 'POST',
body: formData
})
.then((response) => response.json())
.then((responseJson) => {
console.log("JJJJJJJJJJJJJJJJJJJJJJJJJ name: " + responseJson.name);
AsyncStorage.setItem('name', responseJson.name);
this.props.navigation.navigate('Home');
})
.catch(() => {
console.log("JJJJJJJJJJJJJJJJJJ Wrong connection");
alert('Wrong connection');
})
}
}
This is home.js:
export default class Home extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.text}> Member area. You are logged in. </Text>
<TouchableOpacity
style={styles.btn}
onPress={this.logout}>
<Text>Log out</Text>
</TouchableOpacity>
</View>
);
}
logout = () => {
AsyncStorage.removeItem('name');
this.props.navigation.navigate('Login');
console.log("JJJJJJJJJJJJJJJJJJ Logged out");
}
}
Create your navigator in here:
value = AsyncStorage.getItem('name');
if (value !== null) {
InitialScreen = 'Home';
const RoutesNavigation = StackNavigator({
Login: { screen: Login },
Home: { screen: Home }
},{
initialRouteName: InitialScreen,
navigationOptions: {
header: false,
}
});
}
Because you are creating your navigator at the top with empty initial route but you are changing value in here so you must create here.
Hope it will work.
AsyncStorage is async.Because of the js nature thread won't wait result of this
AsyncStorage.getItem('name');
use callback with getItem
AsyncStorage.getItem('name',(error,result) => {
if (result!== null) {
//do something
}
});

React-native Fetch with dynamic params

I need to make a request where I pass parameters in the URL that are dynamic. First I display a list of stores and when the user clicks on an I show the details of it, for this I am using the code below, but even after trying too much I can not get the id.
TypeError: undefined is not an object (evaluating '_this.props.navigation')
This is my service:
import api from '../../../config/api';
export const fetchGetById = () => {
return (
fetch(`${api.API_URL}stores/v1/getById?id=${this.props.navigation.state.params.id}`, {
method: 'get',
headers: {
mall: api.API_MALL,
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(res => res.json())
.then(data => data.result.store)
.catch(err => err)
);
}
export default fetchGetById;
This is my component, in ID: { this.props.navigation.state.params.id } i get params.id correctly:
import React, { Component } from 'react';
import {
View,
Text,
ActivityIndicator,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Styles from './view-component-styles';
import Theme from '../../../config/theme';
type Props = {
error: boolean,
loading: boolean,
data: Object,
fetchData: Function,
};
class ViewComponent extends Component<Props> {
constructor() {
super();
this.goToPage = this.goToPage.bind(this);
}
componentDidMount() {
this.props.fetchData();
}
goToPage(param, id) {
this.props.navigation.navigate(param, { id: id });
}
render() {
const hasData = Object.keys(this.props.data).length;
const errorMessage = () => {
return (
<Text>Ops! Ocorreu um erro ao carregar os dados.</Text>
);
};
const renderLoading = () => {
return (
<View style={Styles.renderLoading}>
<ActivityIndicator color={Theme.color.secondary} size="large" />
<Text style={Theme.text}>Aguarde</Text>
</View>
);
};
const getData = (data) => {
return (
<View>
<Text>ID: { this.props.navigation.state.params.id }</Text>
<Text>{ data.fantasy_name }</Text>
</View>
);
};
return (
<View style={Styles.container}>
{ this.props.loading ? renderLoading() : null }
{ this.props.error ? errorMessage() : null }
{ hasData ? getData(this.props.data) : null }
</View>
);
}
}
export default ViewComponent;
You can try used
export const fetchGetById = ( myid ) => {
return (
fetch(`${api.API_URL}stores/v1/getById?id=myid`, {
method: 'get',
headers: {
mall: api.API_MALL,
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(res => res.json())
.then(data => data.result.store)
.catch(err => err)
);
}

how do I dynamically render title as component

I have a scene:
<Scene key="myFeed" component={myFeed} renderTitle={level}/>
In which I use the component to display the "title".
This function:
const level = () => {
return (
<Level/>
)
}
This component:
export class Level extends Component {
constructor(props) {
super(props);
this.state = {
progressBar: {},
loading: true,
};
}
connection() {
myApi.getServer('/user/level/', (data) => {
this.setState({loading: false, progressBar: data});
}, (err) => {
console.log(err.message);
this.setState({loading: false});
})
}
render() {
return(
<View style={styles.topBarCenterSlider}>
<View style={styles.topBarSlider}>
<View style={[styles.topBarStatus, {width: this.state.progressBar.level["progress"] + '%' : 0}]}/>
</View>
</View>
</View>
);
}
}
I need to change the component after the server response.
Help please, and sorry for my English))
I see you dont invoke "connection()". Put in componentWiLlMount
componentWillMount() {
this.connection();
}

maximum call stack size exceed

Guys i have a main component like this
export default class mobile extends Component {
constructor(props){
super(props);
this.state = {
loading: true
}
}
async componentWillMount(){
let defaultKey = 'login';
const token = await AsyncStorage.getItem('token');
if (token){
console.log('token');
const storedUser = await AsyncStorage.getItem('user');
const payload = JSON.parse(storedUser);
store.dispatch({
type: 'LOGIN_SUCCESS',
payload
})
defaultKey = 'home';
}
const config = await AsyncStorage.getItem('config');
console.log(config);
if ((process.env.NODE_ENV === "development") && !config) {
defaultKey = 'setup';
}
this.setState({loading: false});
Actions[defaultKey]();
}
render() {
return(
<Provider store={ store }>
<Router>
<Scene key="root">
<Scene key="setup"
title="Configuration"
component={ ConfigComponent } />
<Scene key="login"
title="Login"
component={ Login } />
<Scene key="home"
title="Home"
component={ Home } />
<Scene key="signup"
title="Signup"
component={ Signup }/>
</Scene>
</Router>
</Provider>
)
}
};
and another config component which basically takes TextInput value and after successful submission it navigates to login page with
Action.login() where login is key name of login scene
I am not sure when i call Action.login() after successful submission inside fetch post success callback.it throws me error "maximum call size exceeded".My simulator freezes and after many seconds i can see my login view.I tried to call Action.login() outside of fetch call, but same issue. Please look at handleFormSubmit() function.
import React, { Component } from 'react';
import update from 'react-addons-update';
import { View, Text, TextInput, Button, ScrollView, TouchableHighlight, Platform } from 'react-native';
import { Actions, ActionConst } from 'react-native-router-flux';
import { ProgressIndicator } from '../common-scenes/progress';
import { Styles } from '../../app/common/styles';
import { AUTH } from '../../app/common/enums';
import { alert } from '../../app/common/alert';
import { asyncStorage } from '../../app/common/helper';
import { InputBox } from './inputComponent';
export default class ConfigComponent extends Component{
constructor(){
super();
this.state = {
formObject: {
APIURL: 'http://localhost:3000',
MongodbURL: 'mongodb://localhost:27017/react_app',
FacebookAppID: '1780990305515917',
FacebookClientSecret: 'cc7341b671731df7efe44add63b1c79e',
FacebookDisplayName: 'Wish',
Platform: Platform.OS
},
errors:{
APIError: '',
FacebookError: '',
MongodbError: ''
},
test:{
API: false,
Facebook: false,
MongoDB: false
},
processFacebook: false,
processMongoDb: false,
processAPI: false
}
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.apiHandler = this.apiHandler.bind(this);
this.facebookHandler = this.facebookHandler.bind(this);
this.mongodbHandler = this.mongodbHandler.bind(this);
this.updateState = this.updateState.bind(this);
};
handleFormSubmit() {
if (this.state.test.Facebook) {
fetch(AUTH.CONFIG, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(this.state.formObject)
})
.then((response) => response.json())
.then((responseData) => {
asyncStorage('config', this.state.formObject)
.then((result) =>{
Actions.login({type: ActionConst.PUSH});
alert(responseData.message);
});
});
}
};
mongodbHandler(){
this.setState({processMongoDb: false})
var errors;
if (!this.state.formObject.MongodbURL){
errors = update(this.state.errors, {
'MongodbError':{
$set: 'Enter your mongodb connection string'
}
});
}else{
errors = update(this.state.errors, {
'MongodbError':{
$set: ''
}
});
}
this.setState({errors})
if (this.state.formObject.MongodbURL){
this.setState({processMongoDb: true})
fetch('http://localhost:3000/auth/mongodb/test',{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
MongodbURL: this.state.formObject.MongodbURL
})
})
.then((response) => response.json())
.then((responseData) =>{
let test = Object.assign({}, this.state.test, { MongoDB: true})
this.setState({test})
})
.catch((err) =>{
let test = Object.assign({}, this.state.test, { MongoDB: false})
this.setState({test})
})
}
}
facebookHandler(){
var errors;
this.setState({processFacebook: false})
if (!this.state.formObject.FacebookAppID || !this.state.formObject.FacebookDisplayName ){
errors = update(this.state.errors, {
'FacebookError':{
$set: 'Enter both facebook app id and facebook display name'
}
});
}else{
errors = update(this.state.errors, {
'FacebookError':{
$set: ''
}
})
}
this.setState({errors})
if (this.state.formObject.FacebookAppID
&& this.state.formObject.FacebookDisplayName
&& this.state.formObject.FacebookClientSecret){
this.setState({processFacebook: true})
fetch(AUTH.GRAPH,{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
FacebookAppID: this.state.formObject.FacebookAppID,
FacebookDisplayName: this.state.formObject.FacebookDisplayName,
FacebookClientSecret: this.state.formObject.FacebookClientSecret
})
})
.then((result) => result.json())
.then((responseData) => {
let test = Object.assign({}, this.state.test, { Facebook: true});
this.setState({test});
})
.catch((error) => {
let test = Object.assign({}, this.state.test, { Facebook: false});
this.setState({test});
})
}
};
apiHandler() {
let { APIURL } = this.state.formObject;
if(APIURL) {
this.setState({processAPI: true});
fetch(APIURL, {
method: 'HEAD'
})
.then(() => {
let test = Object.assign({}, this.state.test, { API: true });
this.setState({test});
})
.catch((error) => {
let test = Object.assign({}, this.state.test, { API: false });
this.setState({test});
})
}
};
updateState(id, text) {
let newValues = update(this.state.formObject, {
[id]: {
$set: text
}
})
this.setState({formObject: newValues})
};
render(){
return(
<ScrollView style={Styles.container}>
<View style={Styles.innerView}>
<InputBox id="APIURL" placeholder="API URL" title="API URL" updateState={this.updateState}/>
{(this.state.test.API && this.state.processAPI) && <Text style={{color: 'green'}}>Test passed</Text>}
{(!this.state.test.API && this.state.processAPI) && <Text style={{color: 'red'}}>Test Failed</Text>}
<TouchableHighlight style={Styles.button} onPress={this.apiHandler}>
<Text style={Styles.buttonText}>Test API</Text>
</TouchableHighlight>
</View>
<View style={Styles.innerView}>
<InputBox id="MongodbURL" placeholder="Mongodb URL" title="Mongodb Configuration" updateState={this.updateState}/>
<Text style={{color: 'red'}}>{this.state.errors.MongodbError}</Text>
{(this.state.test.MongoDB && this.state.processMongoDb) && <Text style={{color: 'green'}}>Test passed</Text>}
{(!this.state.test.MongoDB && this.state.processMongoDb) && <Text style={{color: 'red'}}>Test Failed</Text>}
<TouchableHighlight style={Styles.button} onPress={this.mongodbHandler}>
<Text style={Styles.buttonText}>Test Mongodb</Text>
</TouchableHighlight>
</View>
<View style={Styles.innerView}>
<InputBox id="FacebookAppID" placeholder="Facebook Client ID" title="Facebook Configuration" updateState={this.updateState} />
<InputBox id="FacebookClientSecret" placeholder="Facebook Client Secret" updateState={this.updateState} />
<InputBox id="FacebookDisplayName" placeholder="Facebook Display Name" updateState={this.updateState} />
<Text style={{color: 'red'}}>{this.state.errors.FacebookError}</Text>
{(this.state.test.Facebook && this.state.processFacebook) && <Text style={{color: 'green'}}>Test passed</Text>}
{(!this.state.test.Facebook && this.state.processFacebook) && <Text style={{color: 'red'}}>Test Failed</Text>}
<TouchableHighlight style={Styles.button} onPress={this.facebookHandler}>
<Text style={Styles.buttonText}>Test facebook</Text>
</TouchableHighlight>
</View>
<View>
{this.state.test.MongoDB && this.state.test.Facebook && <TouchableHighlight style={Styles.button} onPress={this.handleFormSubmit} underlayColor='#99d9f4'>
<Text style={Styles.buttonText}>Submit</Text>
</TouchableHighlight> }
</View>
</ScrollView>
)
}
}
This error is usually encountered when you enter an infinite loop. I believe you are calling this.setState({...}); in a method that triggers a re-render.

Resources