FullStack Labs

Please Upgrade Your Browser.

Unfortunately, Internet Explorer is an outdated browser and we do not currently support it. To have the best browsing experience, please upgrade to Microsoft Edge, Google Chrome or Safari.
Upgrade

Creating a React Native App With a Ruby On Rails Backend (Part 3 of 3)

Written by 
Armaiz Adenwala
,
Mid-Level Software Engineer
Creating a React Native App With a Ruby On Rails Backend (Part 3 of 3)
blog post background
Debugging in React Native: Flipper vs React Native Debugger vs Reactotron
Agile Test Management - Zephyr for JIRA
The Difference Between MongoDB and Firestore, and When You Should Use Each

In the previous article, we set up React Native and Redux. In the final installment of this series, we will complete the app by building out the UI and connecting it to Redux

Note: Don’t forget to keep the Ruby on Rails API running in a separate terminal tab so you can test your connection throughout the tutorial.

Container and Presentational Components

Most React apps that use Redux follow a similar pattern of separating components that interact with Redux and components that manage the UI. You can read more about this distinction in Dan Abramov’s article Presentational and Container Components. To follow this pattern, we will create two files for the home screen homeContainer and home in separate folders. The presentational file, home.js, will handle note display and creation, while homeContainer.js, will connect home.js to the reducer and actions. Create these files below:

-- CODE language-jsx keep-markup --
$ mkdir -p app/containers/ app/screens/
$ touch app/containers/homeContainer.js app/screens/home.js

This results in the file structure shown below:

-- CODE language-jsx keep-markup --
app/  
  containers/      
    homeContainer.js  
  screens/      
    home.js

Creating the Container Component

Redux provides us with a method called connect which takes two functions: mapStateToProps and mapDispatchToProps. These functions provide the connect method with our home screen’s reducers and actions.

We can leverage the mapStateToProps function to pass notes from the Redux state to our presentational component, home.

-- CODE language-jsx keep-markup --
import {bindActionCreators} from 'redux';
import * as notesActions from '../actions/notesActions';

const mapStateToProps = state => ({  
  notes: state.notes,
});

The mapDispatchToProps function works similarly to the mapStateToProps by passing actions to our presentational component. To provide each action with the dispatch method, wrap the actions object in the bindActionCreators method. Check out the Redux docs page on bindActionCreators to learn more about the method.

-- CODE language-jsx keep-markup --
import {bindActionCreators} from 'redux';
import * as notesActions from '../actions/notesActions';

const mapStateToProps = state => ({  
  notes: state.notes,
});

const actions = {  
  ...notesActions,
};

const mapDispatchToProps = dispatch => ({  
  actions: bindActionCreators(actions, dispatch),
});

Finally, import the home screen component and pass mapStateToProps and mapDispatchToProps to Redux’s connect method.

-- CODE language-jsx keep-markup --
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import Home from '../screens/home';
import * as notesActions from '../actions/notesActions';

const actions = {  
  ...notesActions,
};

const mapStateToProps = state => ({  
  notes: state.notes,
});

const mapDispatchToProps = dispatch => ({  
  actions: bindActionCreators(actions, dispatch),
});

exportdefault connect(  
  mapStateToProps,  
  mapDispatchToProps,
)(Home);

Displaying Notes

Open the app/screens/home.js file and return a blank ScrollView.

-- CODE language-jsx keep-markup --
import React, {Component} from 'react';
import {  ScrollView,} from 'react-native';

export default class Home extends Component {  
  render() {    
    return (      
      <ScrollView></ScrollView>    
    );  
  }
}

We should fetch our notes when the component loads using the componentDidMount method. This will make the notes available for display when the user first navigates to the view.

-- CODE language-jsx keep-markup --
componentDidMount() {  
  this.props.actions.fetchNotes();
}

Once fetchNotes returns, our component will have access to the list of notes through this.props.notes.data. The next step is to display the notes by creating _renderNote(note) and _renderNotes() methods. These are underscore methods, which are used to improve readability by indicating that a method is internal.

_renderNote(note) takes a note and returns a <Text> component with the note’s text.

-- CODE language-jsx keep-markup --
_renderNotes() {  
  const {data} = this.props.notes;  
  return<View>{data.map(note => this._renderNote(note))}</View>;
}

Now, call this.renderNotes() to display all notes in the ScrollView.

-- CODE language-jsx keep-markup --
render() {  
  return (    
    <ScrollView style={styles.container}>      
      {this._renderNotes()}    
    </ScrollView>  
  );
}

The state of the fetchNotes action is stored in the status value of our notes object. We can leverage this status data to add note loading and error states to our notes display.

When the status is failure, return <Text>{'Error'}</Text>. When the status is loading, return <Text>{'Loading'}</Text>.

-- CODE language-jsx keep-markup --
_renderNotes() {  
  const {data, status} = this.props.notes;  
  
  if (status === 'failure') {    
    return<Text>{'Error'}</Text>;  
  } elseif (status == 'loading') {    
    return<Text>{'Loading'}</Text>;  
  }  

  return<View>{data.map(note => this._renderNote(note))}</View>;
}

Handling User Input

In order to create a note, we need to gather text input from the user. Create a new internal method _renderCreateForm() that returns a TextInput component from the React Native Core.

Note: The onChangeText prop updates the component's state when a user types in an input field.

-- CODE language-jsx keep-markup --
_renderCreateForm() {  
  return (    
    <View>
      <TextInput        
        placeholder={'Text'}        
        onChangeText={text => this.setState({text})}        
        value={this.state.text}      
      />
    </View>  
  );
}

The value prop is set to the internal state value text. We can initialize this value in the constructor(props) function.

-- CODE language-jsx keep-markup --
constructor(props) {  
  super(props);  
  this.state = {    
    text: '',  
  };
}

Creating a Note

Create the Button component:

-- CODE language-jsx keep-markup --
_renderCreateForm() {  
  return (    
    <View>
      <TextInput        
        placeholder={'Text'}        
        onChangeText={text => this.setState({text})}        
        value={this.state.text}      
      />
      <Button title="Create" onPress={this._createNote} />
    </View>  
  );
}

Now, let's implement the _createNote function.

First, pass the note to the createNote Redux action we created in the last article. Then, set the state’s test property to a blank string to reset the TextInput component. After the createNote action returns, the component will automatically rerender when this.props.notes.status is updated.

-- CODE language-jsx keep-markup --
_createNote = () => {  
  const {text} = this.state;  
  const note = {text};  
  this.props.actions.createNote(note);  
  this.setState({text: ''});
}

Add the _renderCreateForm method to the component’s render method to display the form.

-- CODE language-jsx keep-markup --
render() {  
  return (    
    <ScrollView style={styles.container}>      
      {this._renderNotes()}      
      {this._renderCreateForm()}    
    </ScrollView>  
  );
}

Let's add some quick styling to the TextInput and ScrollView, for aesthetic purposes. For simplicity's sake, we will declare styles in our existing file, instead of creating a new one.

Import StyleSheet from react-native.

-- CODE language-jsx keep-markup --
import {  
  ...
StyleSheet,
} from 'react-native';

Add styles.textfield to the TextInput component.

-- CODE language-jsx keep-markup --
<TextInput  
  style={styles.textfield}  
  placeholder={'Text'}  
  onChangeText={text =>this.setState({text})}  
  value={this.state.text}
/>

Add styles.container to the ScrollView component.

-- CODE language-jsx keep-markup --
<ScrollView style={styles.container}>  
  ...
</ScrollView>

Create a styles object at the bottom of the file.

-- CODE language-jsx keep-markup --
exportdefaultclassHomeextendsComponent {    
  ...
}

const styles = StyleSheet.create({  
  container: {    
    marginTop: 64,    
    marginHorizontal: 16,  
  },  
  textfield: {    
    backgroundColor: '#eee',    
    padding: 16,    
    marginTop: 8,  
  },
});

Our final file should now look like:

-- CODE language-jsx keep-markup --
import React, {Component} from 'react';
import {  
  ScrollView,  
  View,  
  Text,  
  StyleSheet,  
  TextInput,  
  Button,
} from 'react-native';

export default class Home extends Component {  
  constructor(props) {    
    super(props);    
    this.state = {      
      text: '',    
    };  
  }  

  componentDidMount() {    
    this.props.actions.fetchNotes();  
  }

   _createNote = () => {    
    const {text} = this.state;    
    const note = {text};    
    this.props.actions.createNote(note);    
    this.setState({text: ''});  
  }  

  _renderNote(note) {    
    return<Text>{note.text}</Text>;  
  }  

  _renderNotes() {    
    const {data, status} = this.props.notes;    
    if (status === 'failure') {      
      return<Text>{'Error'}</Text>;    
    } elseif (status == 'loading') {      
      return<Text>{'Loading'}</Text>;    
    }    

    return<View>{data.map(note => this._renderNote(note))}</View>;  
  }  

  _renderCreateForm() {    
    return (      
      <View>
        <TextInput          
          style={styles.textfield}          
          placeholder={'Text'}          
          onChangeText={text => this.setState({text})}          
          value={this.state.text}        
        />
        <Button title="Create" onPress={this._createNote} />
      </View>    
    );  
  }  

  render() {    
    return (      
      <ScrollView style={styles.container}>        
        {this._renderNotes()}        
        {this._renderCreateForm()}      
      </ScrollView>    
    );  
  }
}

const styles = StyleSheet.create({  
  container: {    
    marginTop: 64,    
    marginHorizontal: 16,  
  },  
  textfield: {    
    backgroundColor: '#eee',    
    padding: 16,    
    marginTop: 8,  
  },
});

Run react-native run-android or react-native run-ios to preview the final result. You should now be able to see a list of notes, and have the ability to create more notes.

Check out the Github repo for this tutorial to see the completed app.

---

At FullStack Labs, we pride ourselves on our ability to push the capabilities of cutting-edge frameworks like React. Interested in learning more about speeding up development time on your next project? Contact us.

Armaiz Adenwala
Written by
Armaiz Adenwala
Armaiz Adenwala

As a Software Engineer I'm focused on building apps using React Native, React.js, CSS3, HTML5, and Ruby on Rails. I'm currently studying computer science at California State University Sacramento.

FullStack Labs Icon

Let's Talk!

We’d love to learn more about your project. Contact us below for a free consultation with our CEO.
Projects start at $50,000.

company name
name
email
phone
Type of project
Reason for contact
How did you hear about us?
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.