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
Welcome to FullStack Labs. We use cookies to enable better features on our website. Cookies help us tailor content to your interests and locations and provide many other benefits of the site. For more information, please see our Cookies Policy and Privacy Policy.

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

Written by 
,
Creating a React Native App With a Ruby On Rails Backend (Part 3 of 3)
blog post background
Recent Posts
Getting Started with Single Sign-On
Choosing the Right State Management Tool for Your React Apps
Accessibility in Focus: The Intersection of Screen Readers, Keyboards, and QA Testing

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

Table of contents

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></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}">      </scrollview>
      {this._renderNotes()}    
      
  );
}

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></view>
      
        placeholder={'Text'}        
        onChangeText={text => this.setState({text})}        
        value={this.state.text}      
      />
      
  );
}

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></view>
      
        placeholder={'Text'}        
        onChangeText={text => this.setState({text})}        
        value={this.state.text}      
      />
      <button title="Create" onpress="{this._createNote}"></button>
      
  );
}

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}">      </scrollview>
      {this._renderNotes()}      
      {this._renderCreateForm()}    
      
  );
}

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 --

  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></view>
        
          style={styles.textfield}          
          placeholder={'Text'}          
          onChangeText={text => this.setState({text})}          
          value={this.state.text}        
        />
        <button title="Create" onpress="{this._createNote}"></button>
          
    );  
  }  

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

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.

Written by
People having a meeting on a glass room.
Join Our Team
We are looking for developers committed to writing the best code and deploying flawless apps in a small team setting.
view careers
Desktop screens shown as slices from a top angle.
Case Studies
It's not only about results, it's also about how we helped our clients get there and achieve their goals.
view case studies
Phone with an app screen on it.
Our Playbook
Our step-by-step process for designing, developing, and maintaining exceptional custom software solutions.
VIEW OUR playbook
FullStack Labs Icon

Let's Talk!

We’d love to learn more about your project.
Engagements start at $75,000.