How to make external api calls in redux react - express

My code is as below:
const LOAD = 'redux-example/LOAD';
const LOAD_SUCCESS = 'redux-example/LOAD_SUCCESS';
const LOAD_FAIL = 'redux-example/LOAD_FAIL';
import axios from 'axios';
const initialState = {
loaded: false
};
export default function info(state = initialState, action = {}) {
switch (action.type) {
case LOAD:
return {
...state,
loading: true
};
case LOAD_SUCCESS:
return {
...state,
loading: false,
loaded: true,
data: action.result
};
case LOAD_FAIL:
return {
...state,
loading: false,
loaded: false,
error: action.error
};
default:
return state;
}
}
export function load() {
return {
types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],
promise: (client) => client.get('http://example.com/getdata')
};
}
I am using https://github.com/erikras/react-redux-universal-hot-example example as starter kit. I want to make promise based api call to example.com/api.But I am not able to do it with async call.I get error in middleware that can not read promise of undefined.My middleware code is as below.
export default function clientMiddleware(client) {
return ({dispatch, getState}) => {
return next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
const { promise, types, ...rest } = action; // eslint-disable-line no-redeclare
if (!promise) {
return next(action);
}
const [REQUEST,SUCCESS,FAILURE] = types;
next({...rest, type: REQUEST});
const actionPromise = promise(client);
actionPromise.then(
(result) => next({...rest, result, type: SUCCESS}),
(error) => next({...rest, error, type: FAILURE})
).catch((error)=> {
console.error('MIDDLEWARE ERROR:', error);
next({...rest, error, type: FAILURE});
});
return actionPromise;
};
};
}
MY component code is as below
import React, {Component, PropTypes} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {load} from 'redux/modules/info';
#connect(state => ({info: state.info.data}),dispatch => bindActionCreators({load}, dispatch))
export default class InfoBar extends Component {
static propTypes = {
info: PropTypes.object,
load: PropTypes.func.isRequired
}
render() {
const {info, load} = this.props; // eslint-disable-line no-shadow
const styles = require('./InfoBar.scss');
return (
<div className={styles.infoBar + ' well'}>
<div className="container">
This is an info bar
{' '}
<strong>{info ? info.message : 'no info!'}</strong>
<span className={styles.time}>{info && new Date(info.time).toString()}</span>
<button className="btn btn-primary" onClick={load}>Reload from server</button>
</div>
</div>
);
}
}

this is only the reducer. You would want to create an action. An action triggers the event that will make the redux store update its state. The basic flow of redux for something like this goes like:
Mount a component
Dispatch an action
Dispatched action in turn will update the store via the Provider component
this will trigger a re-render of the component.
The following is a basic example using fetch.
import fetch from 'isomorphic-fetch';
export function getUsers() {
return dispatch => {
dispatch({ type: REQUEST_USERS });
return fetch('/api/v1/users')
.then(res => res.json())
.then(users => {
dispatch({ type: RECEIVE_USERS, payload: users });
return users;
});
}
}
Then you can call this in your component level item.
import { getUsers } from 'actions/users';
class UserList extends Component {
componentDidMount() { dispatch(getUsers()) }
}
Check out the example

Related

Api call happens only the first time in react-native redux

I am new to using react native redux and I am facing an issue that the api call is made only once, what if i click on another button which should render a different response based on the params and display it on the component which is a flatlist in my case. Please have a look at my code.
RecordListAction:
import { FETCH_RECORD_LIST, FETCH_RECORD_SUCCESS, FETCH_RECORD_FAILURE } from './types.js'
export const fetchRecordList = () => ({
type: FETCH_RECORD_LIST
})
export const fetchRecordSuccess = json => ({
type: FETCH_RECORD_SUCCESS,
payload: json
})
export const fetchRecordFailure = error => ({
type: FETCH_RECORD_FAILURE,
payload: error
})
export const fetchRecordListApi = () => {
console.log("Now I'm here!")
return async dispatch => {
dispatch(fetchRecordList());
let response = await
fetch(url, {
method: 'POST',
headers: {
'tenantid': '1',
'Content-Type': 'application/json',
'language': '1',
'userid': '11'
},
body: JSON.stringify(global.recordListBody)
}).then((response) => response.json())
.then((responseJson) => {
console.log("RecordList Action Value" + responseJson)
dispatch(fetchRecordSuccess(responseJson.records));
}).catch(error => {
dispatch(fetchRecordFailure(error))
}) }}
recordListReducer.js:
import {FETCH_RECORD_REQUEST,FETCH_RECORD_SUCCESS,FETCH_RECORD_FAILURE}
from "../actions/types"
const initialState = {
isFetching: false,
errorMessage : '',
record :[]
};
const recordListReducer = (state = initialState,action) => {
switch(action.type){
case FETCH_RECORD_REQUEST:
return { ...state, isFetching: true }
case FETCH_RECORD_FAILURE:
return { ...state, isFetching: false, errorMessage: action.payload };
case FETCH_RECORD_SUCCESS:
return{...state, isFetching:false, record:action.payload}
default:
return state
}};
export default recordListReducer;
RecordListContainer.js
import React, { Component } from 'react'
import { Text, View, StyleSheet, ActivityIndicator, Button } from 'react-native'
import PropTypes from 'prop-types';
import {fetchRecordListApi} from "../redux/actions/recordListAction"
import {connect} from "react-redux";
import DetailsViewMode from '../Enums/DetailsViewMode'
import RecordList from '../Components/RecordListComponents/RecordList';
import { Icon, Divider } from 'react-native-elements';
class RecordListContainer extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.props.dispatch(fetchRecordListApi());
}
render(){
let content = <RecordList record = {this.props.recordList.record}/>
if(this.props.recordList.isFetching){
content= <ActivityIndicator size="large" />
}
}}
RecordListContainer.propTypes = {
fetchRecordListApi : PropTypes.func.isRequired,
recordList : PropTypes.object.isRequired}
const mapStateToProps = state =>{
return{
recordList: state.posts
};
}
export default connect(mapStateToProps)(RecordListContainer);
rootReducer.js :
import recordListReducer from './recordListReducers';'
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
posts : recordListReducer,
});
export default rootReducer;
You could make recordListBody part of redux state or react context. Or you could make recordListBody observable and respond to changes. Here is an example of making recordListBody observable:
//object combined with global.recordListBody to add listeners
// and notify them of changes
const recordListBodyObserver = ((observers) => {
const removeObserver = (observer) => () => {
observers = observers.filter((o) => o !== observer);
};
return {
notify: (value) =>
observers.forEach((observer) => observer(value)),
add: (observer) => {
observers.push(observer);
return removeObserver(observer);
},
};
})([]);
let recordListBodyValue;
//your global object with recordListBody that will notify
// listeners if a value for recordListBody is set
const global = {
set recordListBody(value) {
//notify all listeners;
recordListBodyObserver.notify(value);
//set the new value
return (recordListBodyValue = value);
},
get recordListBody() {
return recordListBodyValue;
},
};
//function to create increasing id
const id = ((id) => () => id++)(1);
class App extends React.PureComponent {
componentDidMount() {
this.removeListener = recordListBodyObserver.add(
(value) => {
//you can dispatch your action here using value
// do not use global.recordListBody here becasue
// that has the old valuee
console.log(
'recordListBody changed from:',
global.recordListBody,
'to value:',
value
);
}
);
}
componentWillUnmount() {
//clean up listener when component unmounts
this.removeListener();
}
render() {
return (
<button
onClick={() => (global.recordListBody = id())}
>
Change recordListBody
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am using componentDidUpdate and check if props value is changed, the api is again called when the body coming in props is changed.

Use a global alert across the react-native app

I'm a beginner for react-native and I need to alert to the user based on a status which will be retrieved from an API in every 15 seconds. For this I'm using react-native-background-timer in my main component to call the service. But when app is in some other screen (component) even though the service executes perfectly in the main component, it doesn't update it's props or status depending on the result it received (I guess this should be because I'm in a some other screen and props of main component will not be updated). Due to that alert will not be triggered if app is not in the main component
Can anyone please suggest me an approach for this?
class Home extends Component{
constructor(props){
super(props)
this._onPopUpShowed = this._onPopUpShowed.bind(this)
}
componentDidMount(){
//Initial call after the launch
this.props.fetchLiveOrderData()
//Start timer for polling
const intervalId = BackgroundTimer.setInterval(() => {
isBackgroudLoad=true
this.props.fetchLiveOrderData()
}, 1000*15);
}
render(){
const{payload,isFetching,isError,isSuccess} = this.props.liveOrderData
return(
//Render UI depending on the data fetched
);
}
}
//map state to props
const mapStateToProps = state => {
return {
liveOrderData: state.liveOrderData
}
}
//map dispatch to props
const mapDispatchToProps = dispatch => {
return {
fetchLiveOrderData : () => dispatch(fetchLiveOrderData())
}
}
export default connect(mapStateToProps, mapDispatchToProps) (Home)
liveOrderReducer.js
import {
FETCHING_LIVE_ORDER_DATA, FETCHING_LIVE_ORDER_DATA_SUCCESS, FETCHING_LIVE_ORDER_DATA_ERROR
} from '../constants'
const initialState = {
payload: [],
msg:[],
isFetching: true,
isError: false,
isSuccess: false
}
export default liveOrderReducer = (state = initialState, action) => {
switch(action.type){
case FETCHING_LIVE_ORDER_DATA :
return {
...state,
payload: [],
msg:[],
isFetching: true,
isError: false,
isSuccess: false
}
case FETCHING_LIVE_ORDER_DATA_SUCCESS :
return {
...state,
payload: action.data,
msg:[],
isFetching: false,
isError: false,
isSuccess:true
}
case FETCHING_LIVE_ORDER_DATA_ERROR :
return {
...state,
payload: [],
msg:action.msg,
isFetching: false,
isError: true,
isSuccess:false
}
default:
return state
}
}
index.js
import {
FETCHING_LIVE_ORDER_DATA, FETCHING_LIVE_ORDER_DATA_SUCCESS, FETCHING_LIVE_ORDER_DATA_ERROR
} from '../constants'
import api from '../lib/api'
export const getLiveOrderData = () => {
return {
type : FETCHING_LIVE_ORDER_DATA
}
}
export const getLiveOrderDataSuccess = data => {
return {
type : FETCHING_LIVE_ORDER_DATA_SUCCESS,
data
}
}
export const getLiveOrderDataFailure = () => {
return {
type : FETCHING_LIVE_ORDER_DATA_ERROR
}
}
export const fetchLiveOrderData = () => {
return(dispatch) => {
dispatch(getLiveOrderData())
api.getOrder().then(resp => {
dispatch(getLiveOrderDataSuccess(resp))
}).catch((err) => {
dispatch(getLiveOrderDataFailure(err))
})
}
}
Move the notification code to the container or the root component. This will ensure you will receive notifications even if the user moved away from the home screen.

React native redux props not updated after calling an action

I'm new to react, react native, and redux, I have a react native app that has this login in render
render() {
return (<TouchableOpacity style={Style.btnSubmit} onPress={this.onLoginPressed.bind(this)}>
<Text style={Style.btnText}>Login</Text>
</TouchableOpacity>)
}
and the onLoginPressed function is here
onLoginPressed() {
const { username, password } = this.props.form;
this.props.login({ username, password}); // the login is using the fetch api
// props are not updated with the new state values
console.log(this.props)
}
Everything is working correctly but
the props doesn't update in the onLoginPressed function, however, when I console log the props inside the render function, it's updated.
I understand that redux do a full rendering, but I just don't really understand if it should update the props after calling the login.
Thank you
Update
Here is the end of the component
function mapStateToProps(state) {
return {
...state.login
}
}
function mapDispatchToProps(dispatch) {
return {
login: (formData) => dispatch(login(formData)),
facebookLogin: (formData) => dispatch(facebookLogin(formData)),
setUsername: (username) => dispatch(setUsername(username)),
setPassword: (password) => dispatch(setPassword(password)),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
here is the action
import { Host, Endpoints } from '../config/server';
import { loginActions } from '../config/constants';
/*
* state props
- form
- inProgress
- error
- data
*/
export function login(form) {
return (dispatch) => {
dispatch(loggingIn(true));
fetch(Host + Endpoints.auth.login, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(form)
})
.then(res => res.json())
.then(res => {
dispatch(loggingIn(false));
res.error ? dispatch(loginError(res.error)) :
dispatch(loginSuccess(res.data));
})
.catch(err => dispatch(loginError(err)));
}
}
export function facebookLogin(data) {
return (dispatch) => {
dispatch(loggingIn());
fetch(Host + Endpoints.auth.facebookLogin, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => dispatch(loginSuccess(data)))
.catch(err => dispatch(loginError(err)));
}
}
export function setUsername(username) {
return {
type: loginActions.setUsername,
username
}
}
export function setPassword(password) {
return {
type: loginActions.setPassword,
password
}
}
function loginSuccess(data) {
return {
type: loginActions.LoginSuccess,
data
}
}
function loginError(error) {
return {
type: loginActions.LoginError,
error
}
}
function loggingIn(val) {
return {
type: loginActions.LoggingIn,
inProgress: val
}
}
and here is the reducer
import { loginActions } from '../config/constants';
const initialState = {
form: {
username: '',
password: ''
},
data: null,
inProgress: false,
error: null
};
export default function loginReducer(state = initialState, action) {
switch(action.type) {
case loginActions.LoggingIn:
return {
...state,
inProgress: action.inProgress
}
case loginActions.LoginError:
return {
...state,
error: action.error,
}
case loginActions.LoginSuccess:
return {
...state,
inProgress: false,
error: null,
data: action.data
}
case loginActions.setUsername:
return {
...state,
form: {
username: action.username,
password: state.form.password
}
}
case loginActions.setPassword:
return {
...state,
form: {
username: state.form.username,
password: action.password
}
}
default:
return {
...state
}
}
}
and the reducer index file
import { combineReducers } from 'redux';
import login from './login';
const rootReducer = combineReducers({
login
});
export default rootReducer;
and the configureStore file
import { createStore, applyMiddleware } from 'redux'
import reducers from './reducers'
import thunk from 'redux-thunk'
export default function configureStore() {
let store = createStore(reducers, applyMiddleware(thunk))
return store
}
of course the root is wrapped with the provider passing the store.
You are doing console.log in the same function call that dispatch the login actions. That won’t work, because JavaScript is non-blocking, it will not wait for the login action to complete and update the props before calling console.log
Try console.log in something like componentWillReceiveProps.

Dispatching Action inside Fetch

I am making an async call using fetch and then trying to set state by dispatching an action based on the result of the json data returned.
I am using a QR code reader to read a code which is passed to my didScan method.
didScan(code) {
if (this.state.showCamera){
this.props.pushNewRoute('finder');
getAppointment(code)
.then((appointment)=>{
if (appointment.valid){
this.props.appointmentDetails(appointment);
this.props.resetRoute('summary');
}else{
this.props.resetRoute('error');
}
})
.catch((error) => {
this.props.resetRoute('error');
});
this.setState({showCamera: false});
}
}
I am using react-redux to bind my actions to my dispatchers like this:
function bindActions(dispatch){
return {
resetRoute:(route)=>dispatch(resetRoute(route)),
pushNewRoute:(route)=>dispatch(pushNewRoute(route)),
appointmentDetails:(details)=>dispatch(appointmentDetails(details))
}
}
export default connect(null, bindActions)(Scanner);
but when the promise is returned by my getAppointment service it fails when it tries to do the routing.
this.props.resetRoute('summary');
The error is
Possible unhandled promise rejection{id:0}
Reducers may not dispatch actions
None of my reducers dispatch any actions and the code works fine when I take it out of the Promise .then() block.
Here is the simple getAppointment fetch service for completeness:
export function getAppointment(id:string) {
return fetch('http://myurl/' + id + '/')
.then((response) => response.json())
.catch((error) => {
console.error(error);
return error;
});
}
Any help greatly appreciated.
I'm not sure what your syntax is for binding the actions, haven't seen it before. Here's a sample of code that I made for a project where I do a get request and then set the response as state:
SearchBar.jsx (this does a http request to Solr and gets a JSON object back, then sets that object as the state)
import React, {Component} from 'react';
import httpClient from 'axios';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {setResponse} from '../actions/index'
class SearchBar extends Component {
constructor(props) {
super(props);
this.search = this.search.bind(this);
}
search() {
let q = document.getElementById('searchbar').value;
httpClient(`/search?q=${q}`, { baseURL: window.location.href })
.then( resp => {
console.log(resp);
this.props.setResponse(resp);
});
}
render() {
return (
<div>
<input type='text' id='searchbar'/>
<button onClick={this.search}>Search</button>
</div>
);
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({setResponse: setResponse}, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchBar);
This is the action:
export const setResponse = (res) => {
return {
type: 'RESPONSE_RECEIVED',
payload: res
}
};
This is the reducer:
export default function (state = null, action) {
switch (action.type) {
case 'RESPONSE_RECEIVED':
return action.payload;
break;
}
return state;
}
Which is exported to a combining function (though there is only one reducer atm):
import {combineReducers} from 'redux';
import ResponseReducer from './reducer-response';
const allReducers = combineReducers({
response: ResponseReducer
});
export default allReducers;

Redux fetch data from api

I am trying to fetch some data from an api using Redux. My code looks like this:
Action:
// Import libraries
import axios from 'axios';
// Import types
import {
GET_ALL_PICKS
} from './types';
export const getAllPicks = ({ token }) => {
const getPicks = (dispatch) => {
axios({
method: 'get',
url: 'http://myapi/',
headers: {
Authorization: `Bearer ${token}`
}
})
.then((response) => {
console.log(response.data); // First log here returns data just fine
dispatch({
type: GET_ALL_PICKS,
payload: response.data
});
})
.catch((error) => {
console.log(error);
});
};
return getPicks;
};
Reducer:
// Import types
import {
GET_ALL_PICKS
} from '../actions/types';
// Set Initial State
const INITIAL_STATE = {
allPicks: {},
loading: false,
error: ''
};
// Make pick reducers
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_ALL_PICKS:
return { ...state, allPicks: action.payload }; // Logging action.payload here returns data just fine
default:
return state;
}
};
Component:
// Import Libraries
import React, { Component } from 'react';
import { Text } from 'react-native';
import { connect } from 'react-redux';
import {
getAllPicks
} from '../actions/picks';
// Make Component
class HomeScreen extends Component {
// Fetch Data
componentWillMount() {
const { token } = this.props;
this.props.getAllPicks({ token });
}
// Test response
componentDidMount() {
console.log(this.props.allPicks); // This log returns empty object, why?!
}
render() {
return (
<Text>Test</Text>
);
}
}
const mapStateToProps = ({ auth, picks }) => {
const { token } = auth;
const { allPicks } = picks;
return {
token,
allPicks
};
};
export default connect(mapStateToProps, { getAllPicks })(HomeScreen);
When I run the app I see the data in the action console.log and if I run a console.log(action.payload) in the reducer I see the data just fine but in component I see an empty array which suggests I'm not hooking up the data in my reducer correctly? Here's a screen shot of the logs:
I have also tried this in my reducer after some Googling:
return Object.assign({}, state, {
allPicks: action.payload
});
but again I got the same result. Can anyone explain to me what I am doing wrong?
You are confusing the component lifecycle and the API lifecycle.
In practice, what's happening is:
componentWillMount
getAllPicks
componentDidMount (at which point, the API didn't return, the picks are empty)
[... wait for the API to return]
then the API returns with the data, but too late
What you need to do then is check for your "picks" state in the render() function, which will be updated each time your state changes (which happens when the API returns), thanks to the connect() function.
You can also check that the picks are updated properly using componentWillUpdate, not componentDidMount which again has nothing to do with the props being updated.