0

Edit: I'm also struggling with understanding what kind of structure I should be going for and how I should pass the navigator between components etc. If you have any critique or feedback about my code that would also be highly appreciated!

So I'm trying to create a newsfeed from my PHP API (which accepts POST-data and returns JSON). The JSON:

{"status":"OK","news":[{"header":"some header","text":"some text"},{"header":"some other header","text":"some other text"},{"header":"yet another header","text":"some even more text"}]}

I want to fetch this JSON into my React Native app and write out each news post as a view. The idea is pretty much a blog. At the moment what I've tried to do isn't working. I can successfully fetch the JSON, however, I don't know how to successfully get the JSON from get_news() in Main_logic.js to render() in index.ios.js and print each news post out as a seperate view element. Any help would be really appreciated!

Here is the render function in my index.ios.js:

render(){

    //Components
    const Header = (
      <TouchableHighlight
      onPress={() => this.toggleSideMenu()}
      style={this.state.login_button_press ? styles.hamburger_pressed : styles.hamburger}
      onHideUnderlay={this._onHideUnderlay.bind(this)}
      onShowUnderlay={this._onShowUnderlay.bind(this)}
      >
        <View>
          <Icon
          name='menu'
          color='#FFFFFF'
          size={40}
          />
        </View>
      </TouchableHighlight>
    )
    const ContentView = (
      <Navigator
        initialRoute={{id: 'news'}}
        configureScene={this._configureScene}
        renderScene={(route, navigator) => {
            //Inloggningssidan
            if(route.id === "login"){
              return(
                <View style={{opacity: this.state.opacity}, styles.container}>
                  <Image resizeMode="center" style={styles.logo} source={require('./app/res/mbab_cmyk.png')} />
                  <TextInput
                    placeholder="Namn"
                    autocorrect={false}
                    style={styles.text_box}
                    onChangeText={(username) => {{GLOBALS.USERNAME = username}}}
                    returnKeyType="next"
                    onSubmitEditing={(event) => {
                      this.refs.password.focus();
                    }}
                  />
                  <TextInput
                    ref="password"
                    placeholder="Lösenord"
                    autocorrect={false}
                    secureTextEntry={true}
                    style={styles.text_box}
                    onChangeText={(password) => {{GLOBALS.PASSWORD = password}}}
                    returnKeyType="go"
                  />
                  <TouchableHighlight style={styles.login_button} onPress={() => MainLogic.login(navigator)}>
                      <Text style={styles.button_text}>Logga in</Text>
                  </TouchableHighlight>
                </View>
              );
            }
            else if(route.id === "news"){
              var news = MainLogic.get_news();

              return(
                <ScrollView style={{opacity: this.state.opacity}, styles.scroll_container}>
                  <Text style={styles.empty_text}>{news}</Text>
                </ScrollView>
              );
            }
            //Test sida
            else{
              return(
                <View style={styles.container}>
                  {Header}
                  <Text>TEST</Text>
                  <TouchableHighlight style={styles.button}>
                    <Text>Tryck på mig</Text>
                  </TouchableHighlight>
                </View>
              );
            }
          }
        }
      />
    )

    //App
    return (
      <View style={{backgroundColor: "#093360", flex: 1}}>
        <View style={{flex: 1, marginTop: 20}}>
          <StatusBar
            barStyle={"light-content"}
          />
          <SideMenu
          isOpen={this.state.sidemenu_is_open}
          style={styles.menu}
          menu={<this.Menu navigator={navigator}/>}
          >
            <View style={styles.header_bar}>
              {Header}
            </View>
            {ContentView}
          </SideMenu>
        </View>
      </View>
    );
  }

I also try to split up my code into components to keep it in a pleasant structure. Index.ios.js:

import GLOBALS from './app/components/globals.js';
const MainLogic = require('./app/components/logic/main_logic.js');

Here is the function get_news() located in Main_logic.js (MainLogic):

get_news: function(){
      fetch(GLOBALS.API_URL, {
        method: "POST",
        headers: {
          'Accept': 'application/x-www-form-urlencoded',
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: "function=news&username=" + GLOBALS.USERNAME + "&password=" + GLOBALS.PASSWORD,
      })
      .then((response) => response.json())
      .then((response) => {
        try{
          if(JSON.stringify(response.status).replace(new RegExp('"', 'g'), '').match("OK")){
            GLOBALS.NEWS = response;
          }
          else{
            return "FEL";
          }
        }
        catch(e){
          AlertIOS.alert("error", e.message);
        }
      })
      .catch((e) => {
        console.warn(e);
      })
      .done();
  }

Edit: In response to amb:

news.js:

import React, { Component } from 'react';
import {
  View,
  Text,
  TouchableHighlight,
  ListView,
} from 'react-native';

import styles from './styles.js';
import GLOBALS from './globals.js';
const MainLogic = require('./logic/main_logic.js');

export default class News extends Component{
  constructor (props) {
    super(props)
    this.state = { news: [] }
  }

  componentDidMount(){
    // Modify your function to return a promise instead of a value
    MainLogic.get_news().then(response => {
      // setState will trigger a render with the received data
      this.setState({news: response});
    })
  }

  _renderRow(rowData, sectionID, rowID, highlightRow){
    return (
      <View>
        <Text>{rowData}</Text>
      </View>
    )
  }

  render(){
    return (
      <ListView
        dataSource={this.state.news}
        renderRow={this._renderRow()}
      />
    );
  }
}

get_news() in main_logic.js:

get_news: async function(){
      fetch(GLOBALS.API_URL, {
        method: "POST",
        headers: {
          'Accept': 'application/x-www-form-urlencoded',
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: "function=news&username=" + GLOBALS.USERNAME + "&password=" + GLOBALS.PASSWORD,
      })
      .then((response) => response.json())
      .then((response) => {
        try{
          if(JSON.stringify(response.status).replace(new RegExp('"', 'g'), '').match("OK")){
            return response.news;
          }
          else{
            return "FEL";
          }
        }
        catch(e){
          AlertIOS.alert("error", e.message);
        }
      })
      .catch((e) => {
        console.warn(e);
      })
      .done();
  }

Getting error undefined is not an object (evaluating 'allRowIDs.length')

1 Answer 1

1

var news = MainLogic.get_news(); is an async call, so if you don't wait it to finish, you will never have anything in your news var.

This block

var news = MainLogic.get_news();
return (
  <ScrollView style={{opacity: this.state.opacity}, styles.scroll_container}>
    <Text style={styles.empty_text}>{news}</Text>
  </ScrollView>
);

will never render any news.

What you should do instead is to have a component that calls the get_news function and changes its state when the response arrives. Take this pseudocode as an example and follow the comment annotations:

class News extends React.Component {
  constructor (props) {
    super(props)
    this.state = { news: [] }
  }

  componentDidMount () {
    // Modify your function to return a promise instead of a value
    MainLogic.get_news().then(response => {
      // setState will trigger a render with the received data
      this.setState({news: response})
    }
  }

  render () {
    // You should use ListView instead of ScrollView, because
    // this.state.news will be an array and it can't be displayed
    // in a Text component.
    return (
      <ScrollView style={{opacity: this.state.opacity}, styles.scroll_container}>
        <Text style={styles.empty_text}>
          {this.state.news}
        </Text>
      </ScrollView>
    )
  }
}
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your answer! I've updated my OP with an edit at the bottom. I've tried to understand your explanation but am stuck once again. I'm not sure if I'm using ListView correctly. Sorry if this is all too simple, like I said I'm new to RN!
You need to debug your code. Please check that you're correctly using the data structure returned from your API. It seems that you're blindly copying and pasting the proposed solution without adapting it to your data structure. I recommend you check what you're inserting in your state and what are you getting in rowData.
Also, it seems that you haven't read the ListView docs because you're not using its data source. Please check the documentation facebook.github.io/react-native/releases/0.39/docs/…
Thanks for all the help amb. I'm trying to understand though I'll admit I feel a little lost. I'll go through the documentation again and keep testing the code. I've read the docs on ListView though I'm having a hard time understanding it.
You need to create a DataSource with the array of your data before sending it to the ListView: const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.state = { dataSource: ds.cloneWithRows(['row 1', 'row 2']), };
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.