Lessons in React-Native from F8 App

ref: http://makeitopen.com/tutorials/building-the-f8-app/planning/

  • Part 1: Planning The App
    • Parse became the best choice for this app’s data backend. In light of the Parse Cloud Code shutdown announcement, we decided to transition to use the newly open-sourced Parse Server and Parse Dashboard projects.
    • there is an existing Parse + React package that provides the necessary binding tools, but there was a problem – due to the vagaries of conference wi-fi and connectivity, the F8 app must be able to work offline. Since Parse + React did not support offline syncing of data when the F8 app was being built, so we had to construct our own offline support.
    • What about GraphQL and Relay? While they work extremely well with React Native, Relay did not (at the time) have built-in support for offline usage, and GraphQL wasn’t supported out of the box by Parse. Building the app using them would have required building APIs for GraphQL-Parse, and hacking together an offline storage method for Relay.
    • Given the above, Redux was the best choice for the app. Redux offers a simple implementation of the Flux architecture, providing more control over how the data can be stored and cached, essentially enabling the app to sync one-way from Parse Cloud.
    • Tech Stack
  • Part 2: Designing an App for Multiple Platforms
    • Before you write any React code, there is a very important concept about it that instructs how you think about every piece of a React app. This concept is that your code should re-use as much as possible.
    • Eg. On iOS, the tab segmentation controls use a rounded button style familiar to iOS users, while the Android version of the same component uses an underline style that better matches that platform. Both controls, however, perform the exact same function. So, visually they’re a little bit different, but this can be dealt with using CSS. So now, we can reuse this component in the app, and it will use appropriate styles for iOS or Android.
    • For components that are widely different, we can take advantage of React Native’s built-in Platform specific extensions. This lets us create two separate components, in this case one named F8TabsView.ios.js, and another named F8TabsView.android.js – React Native will auto-detect and load the correct component for each platform based on this extension naming.
    • React Native contains a live reload debugging feature which triggers a refresh of the app anytime the JavaScript is changed. This can cut down on the design iteration process – make a change to a components styles, and save, and you’ll see the change instantly.
    • But what about a component that looks different under multiple conditions? For example, a button component might have a default style, but also styles for tapped, busy doing a task, completed a task, etc. To avoid having to interact with the app each time, we built in a visual debugging Playground component.  If you want to make use of this, the <Playground> component should be re-usable in any React Native app. To enable it, all we have to do is swap a bit of code in our setup() function to load the <Playground> rather than the actual app
  • Part 3: Integrating Data with React Native
    • Each component in a React app is also aware of two different types of data, each having different roles:
      • props is data which is passed into a component when it is created, used as the ‘options’ which you want for any given component. If you had a button component, an example prop would be the text of the button. A component can’t change its props (ie. they are immutable).
      • state is data which can be changed over time by any component. If the button example above was a login/logout button, then the state would store the current login status of the user, and the button would be able to access this, and modify it if they clicked on the button to change their status.

      In order for React apps to reduce repetition, state is intended to be owned by the highest parent component in the app’s component hierarchy – something we’ll refer to as container components elsewhere in this tutorial. In other words, you wouldn’t actually put the state in the example button component mentioned above, you’d have it in the parent view which contains the button, and then use props to pass relevant parts of the state down to child components. Because of this, data only flows one-way downwards through any given app, which keeps everything fast and modular.

      If you would like, you can read more about the reasons and thinking behind these decisions in the Thinking in Reactguide from the React site.

    • Redux expands on React’s data relationship by introducing the concept of Stores, containers for the app’s state objects, and a new workflow for modifying the state over time:
      • Each Store in a Flux app has a callback function which is registered with a Dispatcher.
      • Views (basically React components) can trigger Actions – basically an object which includes a bunch of data about the thing that just happened (for example, maybe it might include new data that was input into a form in the app) as well as an action type – essentially a constant that describes the type of Action being performed.
      • The Action is sent to the Dispatcher.
      • The Dispatcher propagates this Action to all of the various registered Store callbacks.
      • If the Store can tell that it was affected by an Action (because the action type is related to its data), it will update itself, and therefore the contained state. Once updated, it’ll emit a change event.
      • Special Views called Controller Views (a fancy term for the container components we mentioned above) will be listening for these change events, and when they get one, they know they should fetch the new Store data.
      • Once fetched they call setState() with the new data, causing the components inside the View to re-render.

      You can see how Flux helps to enforce the one-way flow of data within React apps, and it makes the data part of React much more elegant and structured.

    • there are official bindings provided by the react-redux package which make integrating it with React apps much easier.
    • Advantages of Redux:
      • the Store is only concerned with holding the state; the components in the Views are only concerned with displaying data and triggering Actions; Actions are only concerned with indicating that something in the state has changed and including data about what it was; Reducers are only concerned with combining an old state and mutating Actions into a new state. Everything is modular, elegant, and has a clear purpose when it comes to reading the code and understanding it.
      • Actions are the only way to trigger a state change, this centralizes this process away from the UI components, and because they are properly ordered by the Reducer, it prevents race conditions.
      • state becomes essentially immutable, and a series of states, each representing an individual change, are created. This gives you a clear and easily traceable state history within your app.
    • Offline Syncing of Store:  because we’re using Redux, there’s a very simple module we can use with our app called Redux Persist.
    • We’re also using something called Middleware with our Store – we’ll talk more about some of the things we use this for in the testing section, but basically, middleware allows you to fit extra logic in between the point of an Action being dispatched, and when it reaches the Reducer (this is useful for things like logging, crash reporting, asynchronous APIs, etc.).
    • If, for example, you’re interacting with any APIs, you’ll need some asynchronous Action creators. Redux on its own has a fairly complex approach to async, but because we’re using React Native, we have access to ES7 await functionalitywhich simplifies the process
    • because we’re using the Parse + React SDK (specifically the parse/react-native package in it), we have incredibly simple SDK access baked right in.
  • Part 4: Testing a React Native App
  • Appendix I: Running the App Locally
    • MongoDB (needed to run Parse Server locally)
  • Appendix II: Using Relay and GraphQL
    • Firstly, and in very simple terms, Relay is the data framework that lives inside the app, and GraphQL is a query language used within Relay to represent the data schema. GraphQL is also run on a server separately from the app to provide a data source for Relay to interact with (we will be covering the GraphQL server setup in a future tutorial, stay tuned!).
    • Relay isn’t derived from the Flux architecture and is used only with GraphQL, which immediately means a big difference from the Redux model. The Store/Reducer/Component interaction we covered in the data tutorial does not really exist with Relay. It takes a different approach, and removes a lot of the building work you normally need to do when integrating data.
    • With Relay, each React component specifies exactly what data it depends on, using GraphQL. Relay handles everything about fetching that data, providing the component with updates when the data changes, and caching the data client-side. Anytime the app wants to change the data itself, it creates a GraphQL Mutation instead of an Action as with Redux.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s