Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetching data from API in react native using redux

I am working on a react-native project where I am trying to fetch data from an api using axios library and display the data.So,my app first shows a splash screen and it then needs to navigate to a page which consists of tabs.The tabs will contain the data from the api.

So,I am trying to initialize my store in my Homepage which comes after the splash screen.I have my reducer and action defined separately in 2 different files.

App.js file

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { StackNavigator } from 'react-navigation';
import SplashScreen from './src/components/SplashScreen';
import HomeScreen from './src/components/HomeScreen';

const Navigation = StackNavigator({
    Splash: {
      screen: SplashScreen
    },
    Home: {
      screen: HomeScreen
    }
})

export default Navigation;

My SplashScreen component:

import React from 'react';
import { StyleSheet,
         Text,
         View,
         } from 'react-native';

export default class SplashScreen extends React.Component {
  static navigationOptions = {
    header: null
  }

  componentWillMount() {
    setTimeout(() => {
      this.props.navigation.navigate('Home')
    },2000)
  }

  render() {
    return(
        <View style={styles.container}>
          <Text style={styles.welcome}>Splash Screen</Text>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'skyblue'
  },
  welcome: {
    color: '#FFF',
    fontSize: 30
  }
})

My HomeScreen component:

import React from 'react';
import { StyleSheet,
         Text,
         View,
         } from 'react-native';

export default class SplashScreen extends React.Component {
  static navigationOptions = {
    header: null
  }

  componentWillMount() {
    setTimeout(() => {
      this.props.navigation.navigate('Home')
    },2000)
  }

  render() {
    return(
        <View style={styles.container}>
          <Text style={styles.welcome}>Splash Screen</Text>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'skyblue'
  },
  welcome: {
    color: '#FFF',
    fontSize: 30
  }
})




import React from 'react';
import { StyleSheet,
         Text,
         View,
         } from 'react-native';

export default class SplashScreen extends React.Component {
  static navigationOptions = {
    header: null
  }

  componentWillMount() {
    setTimeout(() => {
      this.props.navigation.navigate('Home')
    },2000)
  }

  render() {
    return(
        <View style={styles.container}>
          <Text style={styles.welcome}>Splash Screen</Text>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'skyblue'
  },
  welcome: {
    color: '#FFF',
    fontSize: 30
  }
})

Action.js file

import axios from 'axios';
export const FETCH_DATA = 'fetch_data';

const API = 'https://api.myjson.com/bins/fz62x';

export function fetchData() {
  const request = axios.get(API);

  return dispatch => {
    return request.then((data) => {
        dispatch({
          type: FETCH_DATA,
          payload: data
        })
    })
  }
}

My reducer

import { FETCH_DATA } from './actions';

export default function(state={}, action) {
  switch(action.type) {
    case FETCH_DATA:
      return {
        ...state,
        action.payload
      };

    default:
        return state;
  }
}

Can anyon please tell me if this the correct way? If not, then what is the correct way?

like image 746
pranami Avatar asked Jan 10 '18 00:01

pranami


People also ask

How do I get data from API in react native?

Step to run the application: Open the terminal and type the following command. Output: Now open localhost:300 and in the console, the data is fetched. 3. Async-Await: This is the preferred way of fetching the data from an API.


1 Answers

I would say there is no correct or incorrect way to do this. But I can share kind of a pattern that I usually use.

First I would create separate folders for different files. Actions in actions folder, reducers in reducers folder etc... I would create separate constants.js file and configureStore.js file and put them inside the project root directory.

I would drop Axios library and would just use Fetch API for data fetching. Considering your code I would do the following.

Create configureStore.js file inside your project root directory. I recommend you to use Redux-Thunk. You can find more info from here.

configureStore.js

import { createStore, applyMiddleware } from 'redux';
import app from './reducers';
import thunk from 'redux-thunk';

export default function configureStore() {
    let store = createStore(app, applyMiddleware(thunk))
    return store
}

App.js

import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { StackNavigator } from 'react-navigation';
import configureStore from './configureStore';
import HomeScreen from './screens/HomeScreen';

const NavigationApp = StackNavigator({
    HomeScreen: { screen: HomeScreen }
})

const store = configureStore()

export default class App extends Component {

    render() {
        return (
            <Provider store={store}>
                <NavigationApp />
            </Provider>
        );
    }
}

Let's create constants.js and we place it inside the project root directory.

constants.js

export const FETCHING_TODOS = 'FETCHING_TODOS';
export const FETCH_TODOS_SUCCESS = 'FETCH_TODOS_SUCCESS';
export const FETCH_TODOS_FAILURE = 'FETCH_TODOS_FAILURE';

Now let's move on and create our action file which would be put inside the actions folder. Let's name it something like fetchToDos.js. Let's create a simple function using Fetch API.

fetchToDos.js

import { FETCH_TODOS_SUCCESS, FETCH_TODOS_FAILURE, FETCHING_TODOS } from '../constants';

export function fetchToDos() {

    return (dispatch) => {
        dispatch(getTodos())

        return(fetch('https://api.myjson.com/bins/fz62x'))
        .then(res => res.json())
        .then(json => {

            return(dispatch(getToDosSuccess(json)))
        })
        .catch(err => dispatch(getToDosFailure(err)))
    }
}

function getToDos() {

    return {
        type: FETCHING_TODOS
    }
}

function getToDosSuccess(data) {

    return {
        type: FETCH_TODOS_SUCCESS,
        data
    }
}

function getToDosFailure() {
    return {
        type: FETCH_TODOS_FAILURE
    }
}

fetchToDos.js with Axios

import { FETCH_TODOS_SUCCESS, FETCH_TODOS_FAILURE, FETCHING_TODOS } from '../constants';
import axios from 'axios';

export function fetchToDos() {

    return (dispatch) => {
        dispatch(getUser())
        axios.get('https://api.myjson.com/bins/fz62x')
            .then(function (response) {

                // handle your response here, create an object/array/array of objects etc... 
                // and return it in dispatch(getToDosSuccess(data over here))

                return(dispatch(getToDosSuccess(response.data)))
            })
            .catch(err => dispatch(getToDosFailure(err)))
    }
}

// rest is the same...

Moving on to reducers. Let's create two files - index.js, todos.js and put them inside reducers folder.

todos.js

import { FETCH_TODOS_SUCCESS, FETCH_TODOS_FAILURE, FETCHING_TODOS } from '../constants';

const initialState = {
    todos: [],
    isFetching: false,
    error: false
}

export default function todosReducer(state = initialState, action) {

    switch(action.type) {
        case FETCHING_TODOS:
            return {
                ...state,
                isFetching: true
            }
        case FETCH_TODOS_SUCCESS:
            return {
                ...state,
                isFetching: false,
                todos: action.data
            }
        case FETCH_TODOS_FAILURE:
            return {
                ...state,
                isFetching: false,
                error: true
            }
        default:
            return state
    }
}

index.js

import { combineReducers } from 'redux';
import todos from './todos';

const rootReducer = combineReducers({
    todos
})

export default rootReducer

Basically "heavy lifting" is done. I would only create one screen because let's assume that users would tap back button (Android) while at home screen, they would end up on that splash screen. So in this example I am going to use one screen only.

HomeScreen.js

import React, { Component } from 'react';
import {
    View,
    Text,
    ActivityIndicator
} from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { fetchTodos } from '../actions/fetchTodos';

class HomeScreen extends Component {

    componentDidMount() {

        this.props.fetchTodos()

    }

    render() {

        const { todos, isFetching } = this.props.todos

        if (isFetching) {
            return(
                <View style={{flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
                    <ActivityIndicator size={'large'} /> 
                </View>
            )
        } else {
            return(
                <View style={{flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
                    <Text>todos.length</Text>
                </View>
            )
        }

    }

}

function mapStateToProps(state) {
    return {
        todos: state.todos
    }
}

function mapDispatchToProps(dispatch) {
    return {
        ...bindActionCreators({ fetchTodos }, dispatch)
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)

I really hope that you find this concept useful because I can say from my experience that it helped me a lot when I first get started and it helped me to understand the whole concept of redux much better.

Sorry if there are any typos and errors. I was on a flow.

like image 88
Ivalo Pajumets Avatar answered Sep 20 '22 07:09

Ivalo Pajumets