Section 01 Sourcing Album Data
From the sketch, we noticed we are facing to challenges:
- We need to figure out how to get a list of albums (Album Name, Artist Name, images) to show to the users.
- Once we figure out how we will get access to that list of data we need to figure out how we are going to architect our component to show a list of content to users.
From this api we can get a list of json file http://rallycoding.herokuapp.com/api/music_albums
We are going to use Http Request and AJAX to fetch this data. Once we get the data we need to figure out a way to build a list of data of all the different albums.
Section 02 List Component Boilerplate
Follow the three steps mentioned earlier to create a new component named AlbumList
AlbumList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 1.Import import React from 'react'; import { Text, View } from 'react-native'; // 2.make a component const AlbumList = () => { return ( <View> <Text>Album List</Text> </View> ); }; // 3.Make this component reusable for other parts of the Apps export default AlbumList; |
index.ios.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 1.Import a library to help create component import React from 'react'; import { AppRegistry, View } from 'react-native'; import Header from './src/components/header'; import AlbumList from './src/components/AlbumList'; // 2.Create a component const App = () => ( <View> <Header headerText={'Albums(props)'} /> <AlbumList /> </View> ); // 3.Render it to the device AppRegistry.registerComponent('albums',() => App); |
Section 3 Class Based Components
All the components we made are refer to a functional component. That is they are functions that return some amount of JSX to be display on the device. The only rule of this is a component must return some amount of JSX.To handle fetch those data we are going to have to use a second type of component which is called a class based component.
We are going to make AlbumList component to a class based component to fetch data from api. As for class base component we must import Component form React and a class based component must include at least one method. Here we are going to use render method. The only requirement for render method is that it only returns some amount of JSX.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 1.Import import React, { Component } from 'react'; import { Text, View } from 'react-native'; // 2.make a component class AlbumList extends Component{ render(){ return ( <View> <Text>Album List</Text> </View> ); } } // 3.Make this component reusable for other parts of the Apps export default AlbumList; |
Section 4 LifeCycle Method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// 1.Import import React, { Component } from 'react'; import { Text, View } from 'react-native'; // 2.make a component class AlbumList extends Component{ componentWillMount(){ console.log('componentWillMount in AlbumList'); } render(){ return ( <View> <Text>Album List</Text> </View> ); } } // 3.Make this component reusable for other parts of the Apps export default AlbumList; |
In this part we used componentWillMount which will be automatically executed when the app starts. To show the info in console we could use command + D to execute debug mode and it will open browser automatically to show the info in browser console. By adding a debugger statement in this code, it can generate a break point for debugging.
Section 5 Network Requests
To fetch data from api we are going to make an AJAX request by axios. We can install axios by
1 |
npm install --save axios |
AlbumList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 1.Import import React, { Component } from 'react'; import { Text, View } from 'react-native'; import axios from 'axios'; // 2.make a component class AlbumList extends Component{ componentWillMount(){ axios.get("https://rallycoding.herokuapp.com/api/music_albums") .then(response => console.log(response)); } render(){ return ( <View> <Text>Album List</Text> </View> ); } } // 3.Make this component reusable for other parts of the Apps export default AlbumList; |
Enter debugging mode we can see we have fetched data from api:
Section 6 Component Level State
From the timeline we notice that this app need to display data. To display data on the device we are going to use component level state which handles changing the content that is shown on screen.
Fixing this component will take three steps:
- We are going to set some default or initial state for component.
- When we have fetched our data we are going to tell our component to update
- Make sure our component make use of these component.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import React, { Component } from 'react'; import { Text, View } from 'react-native'; import axios from 'axios'; class AlbumList extends Component{ // 1 state = { albums:[] }; componentWillMount(){ // 2 axios.get("https://rallycoding.herokuapp.com/api/music_albums") .then(response => this.setState({albums:response.data})); } render(){ // 3 console.log(this.state); return ( <View> <Text>Album List</Text> </View> ); } } export default AlbumList; |
After using the state technique, our app timeline has been changed into this:
Further more, some rules of state is listed below:
Rules of State
- Definition of state: a plain javascript object used to record and respond to user-triggered events
- When we need to update what a component shows, call ‘this.setState’
- Only change state with ‘setState’, do not do ‘this.state’
- We only use states with class based component
State VS Props
- Props is for parent to child communication
- State is for component internal record keeping. We use state whenever we want to update some amount of data over time whenever we pass data from a parent or a child.
Section 7 Rendering a list of Components & Display individual component
index.ios.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 1.Import a library to help create component import React from 'react'; import { AppRegistry, View } from 'react-native'; import Header from './src/components/header'; import AlbumList from './src/components/AlbumList'; // 2.Create a component const App = () => ( <View> <Header headerText={'Albums(props)'} /> <AlbumList /> </View> ); // 3.Render it to the device AppRegistry.registerComponent('albums',() => App); |
AlbumList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import React, { Component } from 'react'; import { View } from 'react-native'; import axios from 'axios'; import AlbumDetail from './AlbumDetail'; class AlbumList extends Component{ state = { albums:[] }; componentWillMount(){ axios.get("https://rallycoding.herokuapp.com/api/music_albums") .then(response => this.setState({albums:response.data})); } renderAlbums(){ return this.state.albums.map(album => <AlbumDetail key={album.title} album={album} />); } render(){ console.log(this.state); return ( <View> {this.renderAlbums()} </View> ); } } export default AlbumList; |
AlbumDetail.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 1.import import React from 'react'; import { View, Text } from 'react-native'; // 2 const AlbumDetail = (props) => { return ( <View> <Text>{props.album.title}</Text> </View> ); }; // 3 export default AlbumDetail; |
Section 8 Fantastic Reusable Components
Approach 1:
Approach 2:
We are going to make two reusable component named Card and CardItem. The advantage of this approach is that it can save styling code.
Card.js
1 2 3 4 5 6 7 8 9 |
import React from 'react'; import { View } from 'react-native'; const Card = () => { return ( <View></View> ); } export default Card; |
Notice the only purpose of this component right here is just to make something with some nice styling that looks like a card.
Section 9 Styling a card & Passing component as props
Card.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import React from 'react'; import { View } from 'react-native'; const Card = (props) => { return ( <View style={styles.containerStyle}> {props.children} </View> ); }; const styles = { containerStyle:{ borderWidth: 1, borderRadius: 2, borderColor: '#ddd', borderBottomWidth: 0, shadowColor: '#000', shadowOffset: { width: 0, height: 2}, shadowOpacity: 0.1, shadowRadius: 2, elevation: 1, marginLeft: 5, marginRight: 5, marginTop: 10 } }; export default Card; |
AlbumDetail.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 1.import import React from 'react'; import { Text } from 'react-native'; import Card from './Card'; // 2 const AlbumDetail = (props) => { return ( <Card> <Text>{props.album.title}</Text> </Card> ); }; // 3 export default AlbumDetail; |
Notice that in the card.js, we used prop.children. Any time that we pass a component that we write another component that component will show up on the props object as props.children.
Section 10 Dividing card into section
In the section we are going to make a new reusable component named CardSection. The purpose of CardSection is just to make sure the we’ve got a nice little compact area within the card where we can add some content.
CardSection.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React from 'react'; import { View } from 'react-native'; const CardSection = (props) => { return ( <View style={styles.containerStyle}> {props.children} </View> ); }; const styles = { containerStyle : { borderBottomWidth:1, padding: 5, backgroundColor: '#fff', justifyContent: 'flex-start', flexDirection: 'row', borderColor: '#ddd', position: 'relative' } }; export default CardSection; |
AlbumDetail.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import React from 'react'; import { Text } from 'react-native'; import Card from './Card'; import CardSection from './CardSection'; const AlbumDetail = (props) => { return ( <Card> <CardSection> <Text>{props.album.title}</Text> </CardSection> <CardSection> <Text>{props.album.title}</Text> </CardSection> <CardSection> <Text>{props.album.title}</Text> </CardSection> </Card> ); }; export default AlbumDetail; |