Fetch API in react-native using redux - react-native

I have started redux with react-native. I am trying to fetch data from API listing that data using map. Following is my code to fetch data from API.
App.js
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
const App = () => {
return (
<Provider store={store}>
<ProductList />
</Provider>
);
};
export default App;
productList.js
class ProductList extends React.Component {
componentDidMount() {
this.props.dispatch(fetchProducts());
}
render() {
const { products } = this.props;
return (
<View>
{products.map(product => (
<Text key={product.title}>{product.title}</Text>
))}
</View>
);
}
}
const mapStateToProps = state => ({
products: state.products.items,
});
export default connect(mapStateToProps)(ProductList);
productAction.js
async function getProducts() {
return fetch("https://rallycoding.herokuapp.com/api/music_albums")
.then(res => res.json());
}
export function fetchProducts() {
return async dispatch => {
return getProducts()
.then(json => {
dispatch(fetchProductsSuccess(json));
return json;
})
.catch(error =>
console.log(error)
);
};
}
export const FETCH_PRODUCTS_SUCCESS = "FETCH_PRODUCTS_SUCCESS";
export const fetchProductsSuccess = products => ({
type: FETCH_PRODUCTS_SUCCESS,
payload: { products }
});
productReducer.js
const initialState = {
items: [],
};
export default function productReducer(
state = initialState,
action
) {
switch (action.type) {
case FETCH_PRODUCTS_SUCCESS:
return {
...state,
items: action.payload.products
};
default:
return state;
}
}
rootReducer.js
export default combineReducers({
products
});
It is working perfectly. It is showing the list as well.
But can anyone please tell me is it a correct way if I will use this method in big projects then will it be useful or should I follow some other method? Thanks in advance.

I've not used fetch with react-native, but I think this should work fine. I've used axis though. And it is easy to use
import * as axios from 'axios';
import Constant from './../utilities/constants';
axios.defaults.baseURL = Constant.api_base_url;;
axios.defaults.headers.post['Content-Type'] = 'application/json';
// Axios interceptor for handling common HTTP errors
// Need to use it with reducers
axios.interceptors.response.use(res => res, err => Promise.reject(error));
/**
* HTTP request to search item in The MovieDB
*
* #returns {object | Promise}
*/
const getConfiguration = () => {
return axios.get(`/configuration?${Constant.api_key}`)
}
export { getConfiguration }
You can view complete code https://github.com/SandipNirmal/React-Native-MovieDB.

Related

useEffect not triggered

I'm trying to use redux with useEffect to update/get the redux state but useEffect is totally not running at all but I have no idea what is going on. I can't even get the "hi"
import { useSelector, useDispatch } from 'react-redux';
import { setDisplayLogsheet, getLogsheets } from '../redux/actions';
...
const { displayLogsheet, logsheets } = useSelector(state => state.logsheetReducer);
const dispatch = useDispatch();
useEffect(() => {
console.log("hi")
dispatch(getLogsheets());
dispatch(setDisplayLogsheet(logsheets));
}, []);
Any help please? Thanks
UPDATE: here's more code to understand
App.js:
I have added the store inside the provider
const Stack = createStackNavigator();
export default function App() {
return(
<Provider store={Store}>
<NavigationContainer>
...
<Provider />
}
home.js:
tried to useSelector to get the logsheets and displayLogsheets and useEffect to dispatch, but the the useEffect is totally not running
export default function Home({navigation}) {
const { displayLogsheet, logsheets } = useSelector(state => state.logsheetReducer);
const dispatch = useDispatch();
useEffect(() => {
console.log('getting logsheets...')
dispatch(getLogsheets())
}, [dispatch])
useEffect(() => {
console.log('setting displayLogsheet...')
if(logsheets){
dispatch(setDisplayLogsheet(logsheets))
}
}, [dispatch, logsheets])
console.log(logsheets)
console.log(displayLogsheet)
return (
<>
<SafeAreaView>
<ScrollView>
<HomeTopStack logsheet={displayLogsheets} iterateDocket={iterateDocket} />
<ScanBarcodeButton navigation={navigation} />
{displayLogsheets.data.DO.map(logsheet => (
<TouchableOpacity onPress={() => navigation.navigate('Details', logsheet)}>
<DOCards logsheet={displayLogsheets} />
</TouchableOpacity>
))}
</ScrollView>
</SafeAreaView>
</>
)
}
store.js:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logsheetReducer from './reducers';
const rootReducer = combineReducers({ logsheetReducer });
export const Store = createStore(rootReducer, applyMiddleware(thunk));
reducer.js:
this is the reducer to set display logsheet and also to get the dummy logsheet data
import { SET_DISPLAY_LOGSHEET, GET_LOGSHEETS } from "./actions";
const initialState = {
logsheets: {},
displayLogsheet: {},
}
function logsheetReducer(state = initialState, action) {
switch (action.type) {
case SET_DISPLAY_LOGSHEET:
console.log("inside logsheetReducer, SET_DISPLAY_LOGSHEET")
return { ...state, displayLogsheet: action.payload };
case GET_LOGSHEETS:
console.log("inside logsheetReducer, GET_LOGSHEET")
return { ...state, logsheets: action.payload };
default:
return state;
}
}
export default logsheetReducer;
actions.js:
import CreateFakeLogsheets from "../data/logsheet";
export const SET_DISPLAY_LOGSHEET = 'SET_DISPLAY_LOGSHEET';
export const GET_LOGSHEETS = 'GET_LOGSHEETS';
const logsheets = CreateFakeLogsheets(2,3)
export const getLogsheets = () => {
console.log("inside getLogsheets")
try {
return dispatch => {
dispatch({
type: GET_LOGSHEETS,
payload: logsheets
})
}
} catch (error) {
console.log(error)
}
}
export const setDisplayLogsheet = displayLogsheet => {
console.log("inside setDisplayLogsheets")
return dispatch => {
dispatch({
type: SET_DISPLAY_LOGSHEET,
payload: displayLogsheet
});
}
};
here's most of the code with redux and also the useEffect. Any help please
Without knowing how the rest of the code is structured, I would split the effect in two, like this:
useEffect(() => {
console.log('getting logsheets...')
dispatch(getLogsheets())
}, [dispatch])
useEffect(() => {
console.log('setting displayLogsheet...')
if(logsheets){ // only dispatch this if logsheets have been fetched
dispatch(setDisplayLogsheets(logsheets))
}
}, [dispatch, logsheets])

I am using redux in react native application to fetch and display data but its not updating on data change from backend

I am using Redux in my React-Native application.
I am fetching the data from api call and on success rendoring it on ListItem.
I am able to fetch and display data but data is not auto updating unless and until I revisit the page.
Even values are not storing into the app
I am calling method from actions in constructor and componentDidMount method.
Can you Please check the code and tell me where am I going wrong.
Here is action.js
import {
FETCHING_PRODUCT_REQUEST,
FETCHING_PRODUCT_SUCCESS,
FETCHING_PRODUCT_FAILURE
} from './types';
export const fetchingProductRequest = () => ({
type : FETCHING_PRODUCT_REQUEST
});
export const fetchingProductSuccess = (json) => ({
type : FETCHING_PRODUCT_SUCCESS,
payload : json
});
export const fetchingProductFailure = (error) => ({
type : FETCHING_PRODUCT_FAILURE,
payload : error
});
export const fetchProduct = () => {
return async dispatch => {
dispatch(fetchingProductRequest());
try {
let response = await fetch("http://phplaravel-325095-1114213.cloudwaysapps.com/api/shop/shop");
let json = await response.json();
dispatch(fetchingProductSuccess(json));
} catch(error) {
dispatch(fetchingProductFailure(error));
}
}
}
My reducer.js
import {
FETCHING_PRODUCT_REQUEST,
FETCHING_PRODUCT_SUCCESS,
FETCHING_PRODUCT_FAILURE
} from './../actions/types';
const initialState = {
loading : false,
errorMessage : '',
shops: []
}
const products = ( state = initialState, action ) => {
switch(action.type) {
case FETCHING_PRODUCT_REQUEST :
return { ...state, loading: true} ;
case FETCHING_PRODUCT_SUCCESS :
return { ...this.state, loading: false, shops: action.payload };
case FETCHING_PRODUCT_FAILURE :
return { ...state, loading: false, errorMessage: action.payload};
}
};
export default products;
product.js
import * as React from 'react';
import { FlatList , ActivityIndicator} from 'react-native';
import { ListItem } from 'react-native-elements';
import { fetchProduct } from './../../actions/products';
import { connect } from 'react-redux';
import propTypes from 'prop-types';
class Product extends React.Component {
constructor(props) {
super(props);
this.props.fetchProduct();
this.state = {
loading : true,
shops : '',
isFetching: false,
active : true,
}
}
fetchProducts() {
const shopid = 13;
fetch(`http://phplaravel-325095-1114213.cloudwaysapps.com/api/shop/shop`)
.then(response => response.json())
.then((responseJson)=> {
this.setState({
loading: false,
shops: responseJson
})
alert(JSON.stringify(this.state.shops));
})
.catch(error=>console.log(error)) //to catch the errors if any
}
componentDidMount(){
// this.fetchProducts();
this.props.fetchProduct().then(this.setState({loading : false}));
}
renderItem = ({ item }) => (
<ListItem
title={item.name}
subtitle={item.name}
leftAvatar={{
source: item.avatar && { uri: item.avatar },
title: item.name[0]
}}
bottomDivider
chevron
/>
)
render () {
if(!this.state.loading)
{
if(this.props.shopsInfo.loading)
{
return (
<ActivityIndicator/>
)
}
else
{
return (
<FlatList
vertical
showsVerticalScrollIndicator={false}
data={this.props.shopsInfo.shops}
renderItem={this.renderItem}
/>
)
}
}
else
{
return (
<ActivityIndicator/>
)
}
}
}
Product.propTypes = {
fetchProduct: propTypes.func.isRequired
};
const mapStateToProps = (state) => {
return { shopsInfo: state };
}
function mapDispatchToProps (dispatch) {
return {
fetchProduct: () => dispatch(fetchProduct())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Product);
1. Not updating on data change from backend.
You have to call an api on regular interval to get updated data. Redux implementation doesn't mean it will fetch data from server whenever there is any change.
2. Even values are not storing into the app
If you are expecting redux will store data even if you will close/kill an application than it will not. You have persist data in-order to use it or store it in cache. Take a look at redux-persist
The problem is your passing wrong props in mapStateToProps function.
In reducer your updating the response value in shop props.
In order to get the updated value you need to pass shops property to get the value.
const mapStateToProps = (state) => {
const { shops: state };
return {shops};
}

How to get dispatch result in react native?

I am new to React Native and trying to build an App with login function. Here is the overview of my App.
In my root app.js
const rootReducer = combineReducers({
login: loginReducer,
preload: preloadReducer,
});
const store = createStore(rootReducer, applyMiddleware(ReduxThunk));
export default function App() {
return (
<Provider store={store}>
<MyNavigator/>
</Provider>
);
};
In my LoginScreen.js, I have
import * as authActions from '../store/actions/auth';
const LoginScreen = props => {
const loginHandler = async () => {
try {
// login here
await dispatch(authActions.login(username, password));
// I want to get login result and navigate to different screens
if (alreadyLogin) {
props.navigation.navigate('MainScreen');
} else {
props.navigation.navigate('StartupScreen');
}
}
catch (err) {
console.log(err);
}
}
return (<View style={styles.login}>
<Button title='Login' onPress={loginHandler}
</View>);
}
In the ../store/action/auth.js
export const login = (username, password) ={
return async dispatch => {
const response = await fetch(url);
const resData = await response.json();
let results = JSON.parse(resData.results);
// if login success, I dispatch the token for later use
if (resData.errno===0){
dispatch({ type: LOGIN , result: results });
} else {
dispatch({ type: RESET , result: null });
}
}
}
Can I achieve what I want with current structure? I want to get login result and navigate to different screens in my LoginScreen.js above. Thank you.

Loading parameter from store is undefined

I have a screen with jobs that I fetch from an API. In my screen, I want to show a message until the jobs are fetched, then display them on the screen. I am triggering the jobs fetch in componentDidMount(), then trying to display them in RenderJobs(). The problem is that props.isLoading is undefined for whatever reason.
I am using hardcoded values for the API call at the moment. Once I get the data to display properly, I'll change this.
Here is my JobsComponent:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {fetchJobs} from '../redux/ActionCreators';
const mapStateToProps = state => {
return {
jobs: state.jobs
}
}
const mapDispatchToProps = dispatch => ({
fetchJobs: (address, jobTitle) => dispatch(fetchJobs(address, jobTitle))
});
function RenderJobs(props) {
console.log('In RenderJobs, props is: ' + props.jobsData + ' / ' + props.isLoading);
const renderJobItem = ({item, index}) => {
return (
//UI view to show data
);
}
if (props.isLoading) {
return (
<View>
<Text style={{fontSize: 30, color: colors.white}}>The data is loading...</Text>
</View>
);
}
else if (props.errMess) {
return(
<View>
<Text style={{fontSize: 30, color: colors.white}}>{props.errMess} </Text>
</View>
);
}
else {
return (
//UI view to show data
);
}
}
class Jobs extends Component {
componentDidMount() {
this.props.fetchJobs([26.1410638, 44.4346588], "Developer");
}
render() {
return(
<ScrollView contentContainerStyle={styles.bkg}>
<RenderJobs
jobsData={this.props.jobs}
isLoading={this.props.jobs.isLoading}
errMess={this.props.jobs.errMess}
/>
</ScrollView>
)
}
}
This is my reducer:
import * as ActionTypes from '../ActionTypes';
export const jobs = (state = { isLoading: true,
errMess: null,
jobs:[]}, action) => {
switch (action.type) {
case ActionTypes.GET_JOBS:
return {...state, isLoading: false, errMess: null, jobs: action.payload};
case ActionTypes.JOBS_LOADING:
return {...state, isLoading: true, errMess: null, jobs: []}
case ActionTypes.JOBS_FAILED:
return {...state, isLoading: false, errMess: action.payload};
default:
return state;
}
};
And this is the action creator:
export const fetchJobs = (address, job) => async (dispatch) => {
dispatch(jobsLoading());
var obj = {"origin": [26.1410638, 44.4346588], "job_details": ["Developer"]};
//fetch the data
.then(response => response.json())
.then(jobs => dispatch(addJobs(jobs)))
.catch(error => dispatch(jobsFailed(error.message)));
};
export const addJobs = (jobs) => ({
type: ActionTypes.GET_JOBS,
payload: jobs
});
export const jobsLoading = () => ({
type: ActionTypes.JOBS_LOADING
});
export const jobsFailed = (errmess) => ({
type: ActionTypes.JOBS_FAILED,
payload: errmess
});
I am expecting for 2 things to happen.
In the RenderJobs() function, I am counting on props.isLoading to give me the loading state. However, it is undefined. I can see in the logs that the JOBS_LOADING action is dispatched, and that the jobs data is correctly fetched.
Once the jobs data is fetched, I expect it to be displayed in the UI. However, this is not the case - I just see a blank screen.
Any help will be greatly appreciated!
Looks like you have forgotten to add isLoading in your mapStateToProps.
isLoading will be undefined on first render as you have not defined any default value. You could provide default value using default props.
Jobs.defaultProps = {
jobs: {
isLoading: false
}
}
I found out what the issue was. In my configureStore.js, I had forgotten to add the jobs reducer to the store. Thanks to everyone for your answers!
import {jobTitles} from './reducers/jobTitles';
import {jobs} from './reducers/jobs';
import {persistStore, persistCombineReducers} from 'redux-persist';
import storage from 'redux-persist/es/storage';
export const ConfigureStore = () => {
const config = {
key: 'root',
storage,
debug: true
};
const store = createStore(
persistCombineReducers(config, {
jobTitles,
jobs //I added this line and it fixed the problem!
}),
applyMiddleware(thunk, logger)
);
const persistor = persistStore(store);
return {persistor, store};
}

connect() does not re-render component

a component dispatches an action which modifies the Redux store and the other component should get the changed state to props and rerender.
The thing is, the component gets the props, and they are correct and modified, but the component is never rerendered.
Could someone help, been stuck too much..
Component who uses store:
on mount it does a http request,
and should rerender when the state is changed.
class CalendarView extends Component {
componentDidMount() {
axios.get('http://localhost:3000/api/bookings/get')
.then(foundBookings => {
this.props.getBookings(foundBookings);
})
.catch(e => console.log(e))
}
render() {
return (
<Agenda
items={this.props.items}
selected={this.props.today}
maxDate={this.props.lastDay}
onDayPress={this.props.setDay}
renderItem={this.renderItem}
renderEmptyDate={this.renderEmptyDate}
rowHasChanged={this.rowHasChanged}
/>
);
}
renderItem = (item) => {
return (
<View style={[styles.item, { height: item.height }]}>
<Text>Name: {item.name} {item.surname}</Text>
<Text>Time: {item.time}</Text>
</View>
);
}
renderEmptyDate = () => {
return (
<View style={styles.emptyDate}><Text>This is empty date!</Text></View>
);
}
rowHasChanged = (r1, r2) => {
console.log('hit')
return true;
}
}
const mapStateToProps = (state, ownProps) => {
return {
today: state.app.today,
lastDay: state.app.lastDay,
items: state.app.items
}
}
const mapDispatchToProps = (dispatch) => {
return {
setDay: date => dispatch(appActions.setSelectionDate(date.dateString)),
getBookings: data => dispatch(appActions.getBookings(data)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CalendarView);
Action Dispatching:
dispatches an action which modifies the state
onSubmit = (name, surname, selectionDate, selectionTime) => {
axios.post('http://localhost:3000/api/bookings/create', {
bookerName: name,
bookerSurname: surname,
bookerTime: selectionTime,
date: selectionDate
}).then(savedBookings => {
this.props.createBooking(savedBookings);
this.props.navigator.pop({
animationType: 'slide-down',
});
}).catch(e => console.log(e))
}
const mapStateToProps = state => {
//...
}
const mapDispatchToProps = (dispatch) => {
return {
createBooking: data => dispatch(appActions.createBooking(data))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NewBookingScreen);
Reducer:
case types.CREATE_BOOKING: {
const { date , bookings } = action.savedBookings.data;
let dateArr = state.items;
// formatting a booking how needed
Object.keys(dateArr).forEach(key => {
if (key == date) {
dateArr[key] = [];
bookings.map(oneBooking => {
dateArr[key].push({
name: oneBooking.bookerName,
surname: oneBooking.bookerSurname,
time: oneBooking.bookerTime,
height: Math.max(50, Math.floor(Math.random() * 150))
});
})
}
});
return {
...state,
items: dateArr
};
}
full repo if needed: https://github.com/adtm/tom-airbnb/tree/feature/redux
Thank You in advance!
Your reducer is mutating the state, so connect thinks nothing has changed. In addition, your call to map() is wrong, because you're not using the result value.
Don't call push() on an array unless it's a copy. Also, please don't use any randomness in a reducer.
For more info, see Redux FAQ: React Redux ,Immutable Update Patterns, and Roll the Dice: Random Numbers in Redux .